8000 From 0ef3f8efd7bddc0001c41d540a96a57b52b790f1 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 3 Jan 2012 18:46:06 +0100 Subject: [PATCH 0001/2667] added part 1 --- book/part1.rst | 202 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 book/part1.rst diff --git a/book/part1.rst b/book/part1.rst new file mode 100644 index 00000000000..ab135d41549 --- /dev/null +++ b/book/part1.rst @@ -0,0 +1,202 @@ +Create your own framework... on top of the Symfony2 Components (part 1) +======================================================================= + +Symfony2 is a reusable set of standalone, decoupled, and cohesive PHP +components that solve common web development problems. + +Instead of using these low-level components, you can use the ready-to-be-used +Symfony2 full-stack web framework, which is based on these components... or +you can create your very own framework. This series is about the latter. + +.. note:: + + If you just want to use the Symfony2 full-stack framework, you'd better + read its official `documentation`_ instead. + +Why would you like to create your own framework? +------------------------------------------------ + +Why would you like to create your own framework in the first place? If you +look around, everybody will tell you that it's a bad thing to reinvent the +wheel and that you'd better choose an existing framework and forget about +creating your own altogether. Most of the time, they are right but I can think +of a few good reasons to start creating your own framework: + +* To learn more about the low level architecture of modern web frameworks in + general and about the Symfony2 full-stack framework internals in particular; + +* To create a framework tailored to your very specific needs (just be sure + first that your needs are really specific); + +* To experiment creating a framework for fun (in a learn-and-throw-away + approach); + +* To refactor an old/existing application that needs a good dose of recent web + development best practices; + +* To prove the world that you can actually create a framework on your own (... + but with little effort). + +I will gently guide you through the creation of a web framework, one step at a +time. At each step, you will have a fully-working framework that you can use +as is or as a start for your very own. We will start with simple frameworks +and more features will be added with time. Eventually, you will have a +fully-featured full-stack web framework. + +And of course, each step will be the occasion to learn more about some of the +Symfony2 Components. + +.. tip:: + + If you don't have time to read the whole series, or if you want to get + started fast, you can also have a look at `Silex`_, a micro-framework + based on the Symfony2 Components. The code is rather slim and it leverages + many aspects of the Symfony2 Components. + +Many modern web frameworks call themselves MVC frameworks. We won't talk about +MVC here as the Symfony2 Components are able to create any type of frameworks, +not just the ones that follow the MVC architecture. Anyway, if you have a look +at the MVC semantics, this series is about how to create the Controller part +of a framework. For the Model and the View, it really depends on your personal +taste and I will let you use any existing third-party libraries (Doctrine, +Propel, or plain-old PDO for the Model; PHP or Twig for the View). + +When creating a framework, following the MVC pattern is not the right goal. +The main goal should be the Separation of Concerns; I actually think that this +is the only design pattern that you should really care about. The fundamental +principles of the Symfony2 Components are centered around the HTTP +specification. As such, the frameworks that we are going to create should be +more accurately labelled as HTTP frameworks or Request/Response frameworks. + +Before we start +--------------- + +Reading about how to create a framework is not enough. You will have to follow +along and actually type all the examples we will work on. For that, you need a +recent version of PHP (5.3.8 or later is good enough), a web server (like +Apache or NGinx), a good knowledge of PHP and an understanding of Object +Oriented programming. + +Ready to go? Let's start. + +Bootstrapping +------------- + +Before we can even think of creating our first framework, we need to talk +about some conventions: where we will store our code, how we will name our +classes, how we will reference external dependencies, etc. + +To store our framework, create a directory somewhere on your machine: + +.. code-block: sh + + $ mkdir framework + $ cd framework + +Coding Standards +~~~~~~~~~~~~~~~~ + +Before anyone starts a flame war about coding standards and why the one used +here suck hard, let's all admit that this does not matter that much as long as +you are consistent. For this book, we are going to use the `Symfony2 Coding +Standards`_. + +Components Installation +~~~~~~~~~~~~~~~~~~~~~~~ + +To install the Symfony2 Components that we need for our framework, we are +going to use `Composer`_, a project dependency manager for PHP. First, list +your dependencies in a ``composer.json`` file: + +.. code-block:: json + + # framework/composer.json + { + "require": { + "symfony/class-loader": "2.1.*" + } + } + +Here, we tell Composer that our project depends on the Symfony2 ClassLoader +component, version 2.1.0 or later. To actually install the project +dependencies, download the composer binary and run it: + +.. code-block:: sh + + $ wget http://getcomposer.org/composer.phar + $ # or + $ curl -O http://getcomposer.org/composer.phar + + $ php composer.phar install + +After running the ``install`` command, you must see a new ``vendor/`` +directory that must contain the Symfony2 ClassLoader code. + +.. note:: + + Even if we highly recommend you the use of Composer, you can also download + the archives of the components directly or use Git submodules. That's + really up to you. + +Naming Conventions and Autoloading +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We are going to `autoload`_ all our classes. Without autoloading, you need to +require the file where a class is defined before being able to use it. But +with some conventions, we can just let PHP do the hard work for us. + +Symfony2 follows the de-facto PHP standard, `PSR-0`_, for class names and +autoloading. The Symfony2 ClassLoader Component provides an autoloader that +implements this PSR-0 standard and most of the time, the Symfony2 ClassLoader +is all you need to autoload all your project classes. + +Create and empty autoloader in a new ``autoload.php`` file: + +.. code-block:: php + + register(); + +You can now run the ``autoload.php`` on the CLI, it should not do anything and +should not throw any error: + +.. code-block:: sh + + $ php autoload.php + +.. tip:: + + The Symfony website has more information about the `ClassLoader`_ + component. + +Our Project +----------- + +Instead of creating our framework from scratch, we are going to write the same +"application" over and over again, adding one abstraction at a time. Let's +start with the simplest web application we can think of in PHP:: + + Date: Tue, 3 Jan 2012 23:13:08 +0100 Subject: [PATCH 0002/2667] added a LICENSE file --- LICENSE.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000000..176160d1345 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,4 @@ +This work is licensed under a Creative Commons Attribution-Share Alike 3.0 +Unported License. + +http://creativecommons.org/licenses/by-sa/3.0/ From 8ed4076c0e236aa97233c329ecf034fc0fbc9830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 4 Jan 2012 00:06:13 +0100 Subject: [PATCH 0003/2667] Fixed typo --- book/part1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part1.rst b/book/part1.rst index ab135d41549..fdbb2e8787a 100644 --- a/book/part1.rst +++ b/book/part1.rst @@ -188,7 +188,7 @@ start with the simplest web application we can think of in PHP:: $input = $_GET['name']; - printf('Hello %s', $_GET['name']); + printf('Hello %s', $input); That's all for the first part of this series. Next time, we will introduce the HttpFoundation Component and see what it will brings us. From b239305a9689fd78812ea4dd18fca57b923f74cb Mon Sep 17 00:00:00 2001 From: Jacob Dreesen Date: Wed, 4 Jan 2012 02:30:08 +0100 Subject: [PATCH 0004/2667] Fixed typo --- book/part1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part1.rst b/book/part1.rst index ab135d41549..fb18aa3c5bc 100644 --- a/book/part1.rst +++ b/book/part1.rst @@ -150,7 +150,7 @@ autoloading. The Symfony2 ClassLoader Component provides an autoloader that implements this PSR-0 standard and most of the time, the Symfony2 ClassLoader is all you need to autoload all your project classes. -Create and empty autoloader in a new ``autoload.php`` file: +Create an empty autoloader in a new ``autoload.php`` file: .. code-block:: php From 13ba87c8b8de8a9ec8ea159e231ba8612958fc64 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 4 Jan 2012 06:54:20 +0100 Subject: [PATCH 0005/2667] added a tip about the Composer autoloader --- book/part1.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/book/part1.rst b/book/part1.rst index b937418b3df..7d0b5017bbf 100644 --- a/book/part1.rst +++ b/book/part1.rst @@ -177,6 +177,12 @@ should not throw any error: The Symfony website has more information about the `ClassLoader`_ component. +.. note:: + + Composer automatically creates an autoloader for all your installed + dependencies; instead of using the ClassLoader component, you can also + just require ``vendor/.composer/autoload.php``. + Our Project ----------- From 2c79d420e2768cda70a4bdaf35c51f7b4936d982 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 4 Jan 2012 21:15:15 +0100 Subject: [PATCH 0006/2667] added part 2 --- book/part2.rst | 332 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 book/part2.rst diff --git a/book/part2.rst b/book/part2.rst new file mode 100644 index 00000000000..94057b2880d --- /dev/null +++ b/book/part2.rst @@ -0,0 +1,332 @@ +Create your own framework... on top of the Symfony2 Components (part 2) +======================================================================= + +Before we dive into the code refactoring, I first want to step back and take a +look at why you would like to use a framework instead of keeping your +plain-old PHP applications as is. Why using a framework is actually a good +idea, even for the simplest snippet of code and why creating your framework on +top of the Symfony2 components is better than creating a framework from +scratch. + +.. note:: + + I won't talk about the obvious and traditional benefits of using a + framework when working on big applications with more than a few + developers; the Internet has already plenty of good resources on that + topic. + +Even if the "application" we wrote yesterday was simple enough, it suffers +from a few problems:: + + assertEquals('Hello Fabien', $content); + } + } + +.. note:: + + If our application were just slightly bigger, we would have been able to + find even more problems. If you are curious about them, read the `Symfony2 + versus Flat PHP`_ chapter of the Symfony2 documentation. + +At this point, if you are not convinced that security and testing are indeed +two very good reasons to stop writing code the old way and adopt a framework +instead (whatever adopting a framework means in this context), you can stop +reading this series now and go back to whatever code you were working on +before. + +.. note:: + + Of course, using a framework should give you more than just security and + testability, but the more important thing to keep in mind is that the + framework you choose must allow you to write better code faster. + +Going OOP with the HttpFoundation Component +------------------------------------------- + +Writing web code is about interacting with HTTP. So, the fundamental +principles of our framework should be centered around the `HTTP +specification`_. + +The HTTP specification describes how a client (a browser for instance) +interacts with a server (our application via a web server). The dialog between +the client and the server is specified by well-defined *messages*, requests +and responses: *the client sends a request to the server and based on this +request, the server returns a response*. + +In PHP, the request is represented by global variables (``$_GET``, ``$_POST``, +``$_FILE``, ``$_COOKIE``, ``$_SESSION``...) and the response is generated by +functions (``echo``, ``header``, ``setcookie``, ...). + +The first step towards better code is probably to use an Object-Oriented +approach; that's the main goal of the Symfony2 HttpFoundation component: +replacing the default PHP global variables and functions by an Object-Oriented +layer. + +To use this component, open the ``composer.json`` file and add it as a +dependency for the project: + +.. code-block:: json + + # framework/composer.json + { + "require": { + "symfony/class-loader": "2.1.*", + "symfony/http-foundation": "2.1.*" + } + } + +Then, run the composer ``update`` command: + +.. code-block:: sh + + $ php composer.phar update + +Finally, at the bottom of the ``autoload.php`` file, add the code needed to +autoload the component:: + + registerNamespace('Symfony\\Component\\HttpFoundation', __DIR__.'/vendor/symfony/http-foundation'); + +Now, let's rewrite our application by using the ``Request`` and the +``Response`` classes:: + + get('name', 'World'); + + $response = new Response(sprintf('Hello %s', htmlspecialchars($input, ENT_QUOTES, 'UTF-8'))); + + $response->send(); + +The ``createFromGlobals()`` method creates a ``Request`` object based on the +current PHP global variables. + +The ``send()`` method sends the ``Response`` object back to the client (it +first outputs the HTTP headers followed by the content). + +.. tip:: + + Before the ``send()`` call, we should have added a call to the + ``prepare()`` method (``$response->prepare($request);``) to ensure that + our Response were compliant with the HTTP specification. For instance, if + we were to call the page with the ``HEAD`` method, it would have removed + the content of the Response. + +The main difference with the previous code is that you have total control of +the HTTP messages. You can create whatever request you want and you are in +charge of sending the response whenever you see fit. + +.. note:: + + We haven't explicitly set the ``Content-Type`` header in the rewritten + code as the Response object defaults to ``UTF-8`` by default. + +With the ``Request`` class, you have all the request information at your +fingertips thanks to a nice and simple API:: + + getPathInfo(); + + // retrieve GET and POST variables respectively + $request->query->get('foo'); + $request->request->get('bar', 'default value if bar does not exist'); + + // retrieve SERVER variables + $request->server->get('HTTP_HOST'); + + // retrieves an instance of UploadedFile identified by foo + $request->files->get('foo'); + + // retrieve a COOKIE value + $request->cookies->get('PHPSESSID'); + + // retrieve an HTTP request header, with normalized, lowercase keys + $request->headers->get('host'); + $request->headers->get('content_type'); + + $request->getMethod(); // GET, POST, PUT, DELETE, HEAD + $request->getLanguages(); // an array of languages the client accepts + +You can also simulate a request:: + + $request = Request::create('/index.php?name=Fabien'); + +With the ``Response`` class, you can easily tweak the response:: + + setContent('Hello world!'); + $response->setStatusCode(200); + $response->headers->set('Content-Type', 'text/html'); + + // configure the HTTP cache headers + $response->setMaxAge(10); + +.. tip:: + + To debug a Response, cast it to a string; it will return the HTTP + representation of the response (headers and content). + +Last but not the least, these classes, like every other class in the Symfony +code, have been `audited`_ for security issues by an independent company. And +being an Open-Source project also means that many other developers around the +world have read the code and have already fixed potential security problems. +When was the last you ordered a professional security audit for your home-made +framework? + +Even something as simple as getting the client IP address can be insecure:: + + getClientIp()) { + // the client is a known one, so give it some more privilege + } + +And there is an added benefit: it is *secure* by default. What do I mean by +secure? The ``$_SERVER['HTTP_X_FORWARDED_FOR']`` value cannot be trusted as it +can be manipulated by the end user when there is no proxy. So, if you are +using this code in production without a proxy, it becomes trivially easy to +abuse your system. That's not the case with the ``getClientIp()`` method as +you must explicitly trust this header by calling ``trustProxyData()``:: + + getClientIp(true)) { + // the client is a known one, so give it some more privilege + } + +So, the ``getClientIp()`` method works securely in all circumstances. You can +use it in all your projects, whatever the configuration is, it will behave +correctly and safely. That's one of the goal of using a framework. If you were +to write a framework from scratch, you would have to think about all these +cases by yourself. Why not using a technology that already works? + +.. note:: + + If you want to learn more about the HttpFoundation component, you can have + a look at the `API`_ or read its dedicated `documentation`_ on the Symfony + website. + +Believe or not but we have our first framework. You can stop now if you want. +Using just the Symfony2 HttpFoundation component already allows you to write +better and more testable code. It also allows you to write code faster as many +day-to-day problems have already been solved for you. + +As a matter of fact, projects like Drupal have adopted (for the upcoming +version 8) the HttpFoundation component; if it works for them, it will +probably work for you. Don't reinvent the wheel. + +I've almost forgot to talk about one added benefit: using the HttpFoundation +component is the start of better interoperability between all frameworks and +applications using it (as of today Symfony2, Drupal 8, phpBB 4, Silex, +Midguard CMS, ...). + +.. _`Twig`: http://twig.sensiolabs.com/ +.. _`Symfony2 versus Flat PHP`: http://symfony.com/doc/current/book/from_flat_php_to_symfony2.html +.. _`HTTP specification`: http://tools.ietf.org/wg/httpbis/ +.. _`API`: http://api.symfony.com/2.0/Symfony/Component/HttpFoundation.html +.. _`documentation`: http://symfony.com/doc/current/components/http_foundation.html +.. _`audited`: http://symfony.com/blog/symfony2-security-audit From fcaf268ec2f05fdb4e733854c6764e42454f0917 Mon Sep 17 00:00:00 2001 From: Chris Sedlmayr Date: Thu, 5 Jan 2012 12:01:01 +0000 Subject: [PATCH 0007/2667] Fixes grammatical error --- book/part1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part1.rst b/book/part1.rst index 7d0b5017bbf..d20f073015b 100644 --- a/book/part1.rst +++ b/book/part1.rst @@ -197,7 +197,7 @@ start with the simplest web application we can think of in PHP:: printf('Hello %s', $input); That's all for the first part of this series. Next time, we will introduce the -HttpFoundation Component and see what it will brings us. +HttpFoundation Component and see what it brings us. .. _`documentation`: http://symfony.com/doc .. _`Silex`: http://silex.sensiolabs.org/ From a4f52d95f477b27ee3310ed6bedc32c9f92555cb Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 5 Jan 2012 14:21:20 +0100 Subject: [PATCH 0008/2667] added links to projects --- book/part2.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/book/part2.rst b/book/part2.rst index 94057b2880d..34446b749f4 100644 --- a/book/part2.rst +++ b/book/part2.rst @@ -321,8 +321,8 @@ probably work for you. Don't reinvent the wheel. I've almost forgot to talk about one added benefit: using the HttpFoundation component is the start of better interoperability between all frameworks and -applications using it (as of today Symfony2, Drupal 8, phpBB 4, Silex, -Midguard CMS, ...). +applications using it (as of today `Symfony2`_, `Drupal 8`_, phpBB 4, Silex, +Midgard CMS, `Zikula`_ ...). .. _`Twig`: http://twig.sensiolabs.com/ .. _`Symfony2 versus Flat PHP`: http://symfony.com/doc/current/book/from_flat_php_to_symfony2.html @@ -330,3 +330,9 @@ Midguard CMS, ...). .. _`API`: http://api.symfony.com/2.0/Symfony/Component/HttpFoundation.html .. _`documentation`: http://symfony.com/doc/current/components/http_foundation.html .. _`audited`: http://symfony.com/blog/symfony2-security-audit +.. _`Symfony2`: http://symfony.com/ +.. _`Drupal 8`: http://drupal.org/ +.. _`phpBB 4`: http://www.phpbb.com/ +.. _`Silex`: http://silex.sensiolabs.org/ +.. _`Midgard CMS`: http://www.midgard-project.org/ +.. _`Zikula`: http://zikula.org/ From ee67eee6f343459992cd3847929a2df859b492bc Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 5 Jan 2012 14:26:20 +0100 Subject: [PATCH 0009/2667] fixed markup --- book/part2.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/part2.rst b/book/part2.rst index 34446b749f4..f5a23dddd42 100644 --- a/book/part2.rst +++ b/book/part2.rst @@ -321,8 +321,8 @@ probably work for you. Don't reinvent the wheel. I've almost forgot to talk about one added benefit: using the HttpFoundation component is the start of better interoperability between all frameworks and -applications using it (as of today `Symfony2`_, `Drupal 8`_, phpBB 4, Silex, -Midgard CMS, `Zikula`_ ...). +applications using it (as of today `Symfony2`_, `Drupal 8`_, `phpBB 4`_, +`Silex`_, `Midgard CMS`_, `Zikula`_ ...). .. _`Twig`: http://twig.sensiolabs.com/ .. _`Symfony2 versus Flat PHP`: http://symfony.com/doc/current/book/from_flat_php_to_symfony2.html From 4c2e4f9f5c5efde7d33ee16f63d9d8fe40468c08 Mon Sep 17 00:00:00 2001 From: gnugat Date: Thu, 5 Jan 2012 13:48:30 +0000 Subject: [PATCH 0010/2667] [part2] Fixinng typo: removing one of the double where --- book/part2.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part2.rst b/book/part2.rst index f5a23dddd42..acac7fd9666 100644 --- a/book/part2.rst +++ b/book/part2.rst @@ -272,7 +272,7 @@ your servers:: } Using the ``Request::getClientIp()`` method would have given you the right -behavior from day one (and it would have covered the case where where you have +behavior from day one (and it would have covered the case where you have chained proxies):: Date: Fri, 6 Jan 2012 17:42:39 -0200 Subject: [PATCH 0011/2667] Add missing colon to code-block --- book/part1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part1.rst b/book/part1.rst index d20f073015b..d8d3d66b3b6 100644 --- a/book/part1.rst +++ b/book/part1.rst @@ -88,7 +88,7 @@ classes, how we will reference external dependencies, etc. To store our framework, create a directory somewhere on your machine: -.. code-block: sh +.. code-block:: sh $ mkdir framework $ cd framework From 00e524eb50f2693725b6ff0580a93df561b07a74 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 6 Jan 2012 21:23:22 +0100 Subject: [PATCH 0012/2667] fixed ambiguity (closes #6) --- book/part2.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part2.rst b/book/part2.rst index acac7fd9666..69a98204ebc 100644 --- a/book/part2.rst +++ b/book/part2.rst @@ -193,7 +193,7 @@ charge of sending the response whenever you see fit. .. note:: We haven't explicitly set the ``Content-Type`` header in the rewritten - code as the Response object defaults to ``UTF-8`` by default. + code as the charset of the Response object defaults to ``UTF-8``. With the ``Request`` class, you have all the request information at your fingertips thanks to a nice and simple API:: From 4b39fc03f3dc23230fd5c98e1024413b7fd6c843 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 7 Jan 2012 10:11:34 +0100 Subject: [PATCH 0013/2667] removed the path to composer.json as JSON doe snot support comments but some people copy/paste this line too --- book/part1.rst | 1 - book/part2.rst | 1 - 2 files changed, 2 deletions(-) diff --git a/book/part1.rst b/book/part1.rst index d8d3d66b3b6..8188a0e9afe 100644 --- a/book/part1.rst +++ b/book/part1.rst @@ -110,7 +110,6 @@ your dependencies in a ``composer.json`` file: .. code-block:: json - # framework/composer.json { "require": { "symfony/class-loader": "2.1.*" diff --git a/book/part2.rst b/book/part2.rst index 69a98204ebc..43632a17b84 100644 --- a/book/part2.rst +++ b/book/part2.rst @@ -129,7 +129,6 @@ dependency for the project: .. code-block:: json - # framework/composer.json { "require": { "symfony/class-loader": "2.1.*", From 26be81cf539f6e1b76cee65d2f3cabb0f0e1570f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 7 Jan 2012 18:31:20 +0100 Subject: [PATCH 0014/2667] added part 3 --- book/part3.rst | 248 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 book/part3.rst diff --git a/book/part3.rst b/book/part3.rst new file mode 100644 index 00000000000..bdd627d4430 --- /dev/null +++ b/book/part3.rst @@ -0,0 +1,248 @@ +Create your own framework... on top of the Symfony2 Components (part 3) +======================================================================= + +Up until now, our application is simplistic as there is only one page. To +spice things up a little bit, let's go crazy and add another page that says +goodbye:: + + send(); + +As you can see for yourself, much of the code is exactly the same as the one +we have written for the first page. Let's extract the common code that we can +share between all our pages. Code sharing sounds like a good plan to create +our first "real" framework! + +The PHP way of doing the refactoring would probably be the creation of an +include file:: + + get('name', 'World'); + + $response->setContent(sprintf('Hello %s', 8000 htmlspecialchars($input, ENT_QUOTES, 'UTF-8'))); + $response->send(); + +And for the "Goodbye" page:: + + setContent('Goodbye!'); + $response->send(); + +We have indeed moved most of the shared code into a central place, but it does +not feel like a good abstraction, doesn't it? First, we still have the +``send()`` method in all pages, then our pages does not look like templates, +and we are still not able to test this code properly. + +Moreover, adding a new page means that we need to create a new PHP script, +which name is exposed to the end user via the URL +(``http://example.com/goodbye.php``): there is a direct mapping between the PHP +script name and the client URL. This is because the dispatching of the request +is done by the web server directly. It might be a good idea to move this +dispatching to our code for better flexibility. This can be easily achieved by +routing all client requests to a single PHP script. + +.. tip:: + + Exposing a single PHP script to the end user is a design pattern called + the "`front controller`_". + +Such a script might look like the following:: + + __DIR__.'/hello.php', + '/bye' => __DIR__.'/bye.php', + ); + + $path = $request->getPathInfo(); + if (isset($map[$path])) { + require $map[$path]; + } else { + $response->setStatusCode(404); + $response->setContent('Not Found'); + } + + $response->send(); + +And here is for instance the new ``hello.php`` script:: + + get('name', 'World'); + $response->setContent(sprintf('Hello %s', htmlspecialchars($input, ENT_QUOTES, 'UTF-8'))); + +In the ``front.php`` script, ``$map`` associates URL paths with their +corresponding PHP script paths. + +As a bonus, if the client ask for a path that is not defined in the URL map, +we return a custom 404 page; you are now in control of your website. + +To access a page, you must now use the ``front.php`` script: + +* ``http://example.com/front.php/hello?name=Fabien`` + +* ``http://example.com/front.php/bye`` + +``/hello`` and ``/bye`` are the page *path*s. + +.. tip:: + + Most web servers like Apache or nginx are able to rewrite the incoming + URLs and remove the front controller script so that your users will be + able to type ``http://example.com/hello?name=Fabien``, which looks much + better. + +So, the trick is the usage of the ``Request::getPathInfo()`` method which +returns the path of the Request by removing the front controller script name +including its sub-directories (only if needed -- see above tip). + +.. tip:: + + You don't even need to setup a web server to test the code. Instead, + replace the ``$request = Request::createFromGlobals();`` call to something + like ``$request = Request::create('/hello?name=Fabien');`` where the + argument is the URL path you want to simulate. + +Now that the web server always access the same script (``front.php``) for all +our pages, we can secure our code further by moving all other PHP files +outside the web root directory: + + example.com + ├── composer.json + │ src + │ ├── autoload.php + │ └── pages + │ ├── hello.php + │ └── bye.php + ├── vendor + └── web + └── front.php + +Now, configure your web server root directory to point to ``web/`` and all +other files won't be accessible from the client anymore. + +.. note:: + + For this new structure to work, you will have to adjust some paths in + various PHP files; the changes are left as an exercise for the reader. + +The last thing that is repeated in each page is the call to ``setContent()``. +We can convert all pages to "templates" by just echoing the content and +calling the ``setContent()`` directly from the front controller script:: + + getPathInfo(); + if (isset($map[$path])) { + ob_start(); + include $map[$path]; + $response->setContent(ob_get_clean()); + } else { + $response->setStatusCode(404); + $response->setContent('Not Found'); + } + + // ... + +And the ``hello.php`` script can now be converted to a template:: + + + + get('name', 'World') ?> + + Hello + +We have our framework for today:: + + __DIR__.'/../src/pages/hello.php', + '/bye' => __DIR__.'/../src/pages/bye.php', + ); + + $path = $request->getPathInfo(); + if (isset($map[$path])) { + ob_start(); + include $map[$path]; + $response->setContent(ob_get_clean()); + } else { + $response->setStatusCode(404); + $response->setContent('Not Found'); + } + + $response->send(); + +Adding a new page is a two step process: add an entry in the map and create a +PHP template in ``src/pages/``. From a template, get the Request data via the +``$request`` variable and tweak the Response headers via the ``$response`` +variable. + +.. note:: + + If you decide to stop here, you can probably enhance your framework by + extracting the URL map to a configuration file. + +.. _`front controller`: http://symfony.com/doc/current/book/from_flat_php_to_symfony2.html#a-front-controller-to-the-rescue From 18d376eb1b42f55f216a6e36cd33c624bb806954 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 7 Jan 2012 20:01:15 +0100 Subject: [PATCH 0015/2667] updated composer.json --- book/part1.rst | 2 +- book/part2.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/book/part1.rst b/book/part1.rst index 8188a0e9afe..89670f56904 100644 --- a/book/part1.rst +++ b/book/part1.rst @@ -112,7 +112,7 @@ your dependencies in a ``composer.json`` file: { "require": { - "symfony/class-loader": "2.1.*" + "symfony/class-loader": "master-dev" } } diff --git a/book/part2.rst b/book/part2.rst index 43632a17b84..fce684c3803 100644 --- a/book/part2.rst +++ b/book/part2.rst @@ -131,8 +131,8 @@ dependency for the project: { "require": { - "symfony/class-loader": "2.1.*", - "symfony/http-foundation": "2.1.*" + "symfony/class-loader": "master-dev", + "symfony/http-foundation": "master-dev" } } From a18f8274ea81d5ce7e6076ef987b7e0ba1796bd4 Mon Sep 17 00:00:00 2001 From: jdreesen Date: Sun, 8 Jan 2012 03:19:20 +0100 Subject: [PATCH 0016/2667] fixed typos --- book/part3.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/part3.rst b/book/part3.rst index bdd627d4430..5ae29f79aeb 100644 --- a/book/part3.rst +++ b/book/part3.rst @@ -70,7 +70,7 @@ and we are still not able to test this code properly. Moreover, adding a new page means that we need to create a new PHP script, which name is exposed to the end user via the URL -(``http://example.com/goodbye.php``): there is a direct mapping between the PHP +(``http://example.com/bye.php``): there is a direct mapping between the PHP script name and the client URL. This is because the dispatching of the request is done by the web server directly. It might be a good idea to move this dispatching to our code for better flexibility. This can be easily achieved by @@ -122,7 +122,7 @@ And here is for instance the new ``hello.php`` script:: In the ``front.php`` script, ``$map`` associates URL paths with their corresponding PHP script paths. -As a bonus, if the client ask for a path that is not defined in the URL map, +As a bonus, if the client asks for a path that is not defined in the URL map, we return a custom 404 page; you are now in control of your website. To access a page, you must now use the ``front.php`` script: From 255577f0154ba86190f64ac25fed7331a5ecf7c2 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 8 Jan 2012 21:44:32 +0100 Subject: [PATCH 0017/2667] went back to 2.1.* in composer.json files --- book/part1.rst | 2 +- book/part2.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/book/part1.rst b/book/part1.rst index 89670f56904..8188a0e9afe 100644 --- a/book/part1.rst +++ b/book/part1.rst @@ -112,7 +112,7 @@ your dependencies in a ``composer.json`` file: { "require": { - "symfony/class-loader": "master-dev" + "symfony/class-loader": "2.1.*" } } diff --git a/book/part2.rst b/book/part2.rst index fce684c3803..43632a17b84 100644 --- a/book/part2.rst +++ b/book/part2.rst @@ -131,8 +131,8 @@ dependency for the project: { "require": { - "symfony/class-loader": "master-dev", - "symfony/http-foundation": "master-dev" + "symfony/class-loader": "2.1.*", + "symfony/http-foundation": "2.1.*" } } From 358b4c801b333a23c656caaaa926cb0b456ff6bf Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 9 Jan 2012 09:24:32 +0100 Subject: [PATCH 0018/2667] added part 4 --- book/part4.rst | 253 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 book/part4.rst diff --git a/book/part4.rst b/book/part4.rst new file mode 100644 index 00000000000..72d86506336 --- /dev/null +++ b/book/part4.rst @@ -0,0 +1,253 @@ +Create your own framework... on top of the Symfony2 Components (part 4) +======================================================================= + +Before we start with today's topic, let's refactor our current framework just +a little to make templates even more readable:: + + 'hello', + '/bye' => 'bye', + ); + + $path = $request->getPathInfo(); + if (isset($map[$path])) { + ob_start(); + extract($request->query->all()); + include sprintf(__DIR__.'/../src/pages/%s.php', $map[$path]); + $response = new Response(ob_get_clean()); + } else { + $response = new Response('Not Found', 404); + } + + $response->send(); + +As we now extract the request query parameters, simplify the ``hello.php`` +template as follows:: + + + + Hello + +Now, we are in good shape to add new features. + +One very important aspect of any website is the form of its URLs. Thanks to +the URL map, we have decoupled the URL from the code that generates the +associated response, but it is not yet flexible enough. For instance, we might +want to support dynamic paths to allow embedding data directly into the URL +instead of relying on a query string: + + # Before + /hello?name=Fabien + + # After + /hello/Fabien + +To support this feature, we are going to use the Symfony2 Routing component. +As always, add it to ``composer.json`` and run the ``php composer.phar +update`` command to install it:: + +.. code-block:: json + + { + "require": { + "symfony/class-loader": "2.1.*", + "symfony/http-foundation": "2.1.*", + "symfony/routing": "2.1.*" + } + } + +From now on, we are going to use the generated Composer autoloader instead of +our own ``autoload.php``. Remove the ``autoload.php`` file and replace its +reference in ``front.php``:: + + add('hello', new Route('/hello/{name}', array('name' => 'World'))); + $routes->add('bye', new Route('/bye')); + +Each entry in the collection is defined by a name (``hello``) and a ``Route`` +instance, which is defined by a route pattern (``/hello/{name}``) and an array +of default values for route attributes (``array('name' => 'World')``). + +.. note:: + + Read the official `documentation`_ for the Routing component to learn more + about its many features like URL generation, attribute requirements, HTTP + method enforcements, loaders for YAML or XML files, dumpers to PHP or + Apache rewrite rules for enhanced performance, and much more. + +Based on the information stored in the ``RouteCollection`` instance, a +``UrlMatcher`` instance can match URL paths:: + + use Symfony\Component\Routing\RequestContext; + use Symfony\Component\Routing\Matcher\UrlMatcher; + + $context = new RequestContext(); + $context->fromRequest($request); + $matcher = new UrlMatcher($routes, $context); + + $attributes = $matcher->match($request->getPathInfo()); + +The ``match()`` method takes a request path and returns an array of attributes +(notice that the matched route is automatically stored under the special +``_route`` attribute):: + + print_r($matcher->match('/bye')); + array ( + '_route' => 'bye', + ); + + print_r($matcher->match('/hello/Fabien')); + array ( + 'name' => 'Fabien', + '_route' => 'hello', + ); + + print_r($matcher->match('/hello')); + array ( + 'name' => 'World', + '_route' => 'hello', + ); + +.. note:: + + Even if we don't strictly need the request context in our examples, it is + used in real-world applications to enforce method requirements and more. + +The URL matcher throws an exception when none of the routes match:: + + $matcher->match('/not-found'); + + // throws a Symfony\Component\Routing\Exception\ResourceNotFoundException + +With this knowledge in mind, let's write the new version of our framework:: + + fromRequest($request); + $matcher = new Routing\Matcher\UrlMatcher($routes, $context); + + try { + extract($matcher->match($request->getPathInfo())); + ob_start(); + include sprintf(__DIR__.'/../src/pages/%s.php', $_route); + + $response = new Response(ob_get_clean()); + } catch (Routing\Exception\ResourceNotFoundException $e) { + $response = new Response('Not Found', 404); + } catch (Exception $e) { + $response = new Response('An error occurred', 500); + } + + $response->send(); + +There are a few new things in the code:: + +* Route names are used for template names; + +* ``500`` errors are now managed correctly; + +* Request attributes are extracted to keep our templates simple:: + + + + Hello + +* Routes configuration has been moved to its own file: + + .. code-block:: php + + add('hello', new Routing\Route('/hello/{name}', array('name' => 'World'))); + $routes->add('bye', new Routing\Route('/bye')); + + We now have a clear separation between the configuration (everything + specific to our application in ``app.php``) and the framework (the generic + code that powers our application in ``front.php``). + +With less than 30 lines of code, we have a new framework, more powerful and +more flexible than the previous one. Enjoy! + +Using the Routing component has one big additional benefit: the ability to +generate URLs based on Route definitions. When using both URL matching and URL +generation in your code, changing the URL patterns should have no other +impact. Want to know how to use the generator? Insanely easy:: + + use Symfony\Component\Routing; + + $generator = new Routing\Generator\UrlGenerator($routes, $context); + + echo $generator->generate('hello', array('name' => 'Fabien)); + // outputs /hello/Fabien + +The code should be self-explanatory; and thanks to the context, you can even +generate absolute URLs:: + + echo $generator->generate('hello', array('name' => 'Fabien), true); + // outputs something like http://example.com/somewhere/hello/Fabien + +.. tip:: + + Concerned about performance? Based on your route definitions, create a + highly optimized URL matcher class that can replace the default + ``UrlMatcher``:: + + $dumper = new Routing\Matcher\Dumper\PhpMatcherDumper($routes); + + echo $dumper->dump(); + + Want even more performance? Dump your routes as a set of Apache rewrite + rules:: + + $dumper = new Routing\Matcher\Dumper\ApacheMatcherDumper($routes); + + echo $dumper->dump(); + +.. _`documentation`: http://symfony.com/doc/current/components/routing.html From 1473eec0c2b98c43ef5fa1d27fb507dbdf7030b2 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 9 Jan 2012 10:01:53 +0100 Subject: [PATCH 0019/2667] fixed typo --- book/part4.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/part4.rst b/book/part4.rst index 72d86506336..8069c75b4df 100644 --- a/book/part4.rst +++ b/book/part4.rst @@ -224,13 +224,13 @@ impact. Want to know how to use the generator? Insanely easy:: $generator = new Routing\Generator\UrlGenerator($routes, $context); - echo $generator->generate('hello', array('name' => 'Fabien)); + echo $generator->generate('hello', array('name' => 'Fabien')); // outputs /hello/Fabien The code should be self-explanatory; and thanks to the context, you can even generate absolute URLs:: - echo $generator->generate('hello', array('name' => 'Fabien), true); + echo $generator->generate('hello', array('name' => 'Fabien'), true); // outputs something like http://example.com/somewhere/hello/Fabien .. tip:: From 7e8da09c2a5a988d859cd6983a33e521eb8fc6bf Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 9 Jan 2012 11:12:48 +0100 Subject: [PATCH 0020/2667] fixed typo (closes #8) --- book/part4.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/book/part4.rst b/book/part4.rst index 8069c75b4df..a7022699ee1 100644 --- a/book/part4.rst +++ b/book/part4.rst @@ -208,6 +208,8 @@ There are a few new things in the code:: $routes->add('hello', new Routing\Route('/hello/{name}', array('name' => 'World'))); $routes->add('bye', new Routing\Route('/bye')); + return $routes; + We now have a clear separation between the configuration (everything specific to our application in ``app.php``) and the framework (the generic code that powers our application in ``front.php``). From bd3ca8e93907ab4649a848696e0146a75df5345f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 9 Jan 2012 11:42:14 +0100 Subject: [PATCH 0021/2667] fixed extract() calls --- book/part4.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/part4.rst b/book/part4.rst index a7022699ee1..056934204ce 100644 --- a/book/part4.rst +++ b/book/part4.rst @@ -23,7 +23,7 @@ a little to make templates even more readable:: $path = $request->getPathInfo(); if (isset($map[$path])) { ob_start(); - extract($request->query->all()); + extract($request->query->all(), EXTR_SKIP); include sprintf(__DIR__.'/../src/pages/%s.php', $map[$path]); $response = new Response(ob_get_clean()); } else { @@ -169,7 +169,7 @@ With this knowledge in mind, let's write the new version of our framework:: $matcher = new Routing\Matcher\UrlMatcher($routes, $context); try { - extract($matcher->match($request->getPathInfo())); + extract($matcher->match($request->getPathInfo()), EXTR_SKIP); ob_start(); include sprintf(__DIR__.'/../src/pages/%s.php', $_route); From db0ad14d469e2cfcfd27c44cefea9b9ca38c4d77 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 11 Jan 2012 08:39:01 +0100 Subject: [PATCH 0022/2667] added part 5 --- book/part5.rst | 190 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 book/part5.rst diff --git a/book/part5.rst b/book/part5.rst new file mode 100644 index 00000000000..088f4402171 --- /dev/null +++ b/book/part5.rst @@ -0,0 +1,190 @@ +Create your own framework... on top of the Symfony2 Components (part 5) +======================================================================= + +The astute reader has noticed that our framework hardcodes the way specific +"code" (the templates) is run. For simple pages like the ones we have created +so far, that's not a problem, but if you want to add more logic, you would be +forced to put the logic into the template itself, which is probably not a good +idea, especially if you still have the separation of concerns principle in +mind. + +Let's separate the template code from the logic by adding a new layer: the +controller: *The controller mission is to generate a Response based on the +information conveyed by the client Request.* + +Change the template rendering part of the framework to read as follows:: + + attributes->add($matcher->match($request->getPathInfo())); + $response = call_user_func('render_template', $request); + } catch (Routing\Exception\ResourceNotFoundException $e) { + $response = new Response('Not Found', 404); + } catch (Exception $e) { + $response = new Response('An error occurred', 500); + } + +As the rendering is now done by an external function (``render_template()`` +here), we need to pass to it the attributes extracted from the URL. We could +have passed them as an additional argument to ``render_template()``, but +instead, let's use another feature of the ``Request`` class called +*attributes*: Request attributes lets you attach additional information about +the Request that is not directly related to the HTTP Request data. + +You can now create the ``render_template()`` function, a generic controller +that renders a template when there is no specific logic. To keep the same +template as before, request attributes are extracted before the template is +rendered:: + + function render_template($request) + { + extract($request->attributes->all(), EXTR_SKIP); + ob_start(); + include sprintf(__DIR__.'/../src/pages/%s.php', $_route); + + return new Response(ob_get_clean()); + } + +As ``render_template`` is used as an argument to the PHP ``call_user_func()`` +function, we can replace it with any valid PHP `callbacks`_. This allows us to +use a function, an anonymous function, or a method of a class as a +controller... your choice. + +As a convention, for each route, the associated controller is configured via +the ``_controller`` route attribute:: + + $routes->add('hello', new Routing\Route('/hello/{name}', array( + 'name' => 'World', + '_controller' => 'render_template', + ))); + + try { + $request->attributes->add($matcher->match($request->getPathInfo())); + $response = call_user_func($request->attributes->get('_controller'), $request); + } catch (Routing\Exception\ResourceNotFoundException $e) { + $response = new Response('Not Found', 404); + } catch (Exception $e) { + $response = new Response('An error occurred', 500); + } + +A route can now be associated with any controller and of course, within a +controller, you can still use the ``render_template()`` to render a template:: + + $routes->add('hello', new Routing\Route('/hello/{name}', array( + 'name' => 'World', + '_controller' => function ($request) { + return render_template($request); + } + ))); + +This is rather flexible as you can change the Response object afterwards and +you can even pass additional arguments to the template:: + + $routes->add('hello', new Routing\Route('/hello/{name}', array( + 'name' => 'World', + '_controller' => function ($request) { + // $foo will be available in the template + $request->attributes->set('foo', 'bar'); + + $response = render_template($request); + + // change some header + $response->headers->set('Content-Type', 'text/plain'); + + return $response; + } + ))); + +Here is the updated and improved version of our framework:: + + attributes->all(), EXTR_SKIP); + ob_start(); + include sprintf(__DIR__.'/../src/pages/%s.php', $_route); + + return new Response(ob_get_clean()); + } + + $request = Request::createFromGlobals(); + $routes = include __DIR__.'/../src/app.php'; + + $context = new Routing\RequestContext(); + $context->fromRequest($request); + $matcher = new Routing\Matcher\UrlMatcher($routes, $context); + + try { + $request->attributes->add($matcher->match($request->getPathInfo())); + $response = call_user_func($request->attributes->get('_controller'), $request); + } catch (Routing\Exception\ResourceNotFoundException $e) { + $response = new Response('Not Found', 404); + } catch (Exception $e) { + $response = new Response('An error occurred', 500); + } + + $response->send(); + +To celebrate the birth of our new framework, let's create a brand new +application that needs some simple logic. Our application has one page that +says whether a given year is a leap year or not. When calling +``/is_leap_year``, you get the answer for the current year, but the you can +also specify a year like in ``/is_leap_year/2009``. Being generic, the +framework does not need to be modified in any way, just create a new +``app.php`` file:: + + add('leap_year', new Routing\Route('/is_leap_year/{year}', array( + 'year' => null, + '_controller' => function ($request) { + if (is_leap_year($request->attributes->get('year'))) { + return new Response('Yep, this is a leap year!'); + } + + return new Response('Nope, this is not a leap year.'); + } + ))); + + return $routes; + +The ``is_leap_year()`` function returns ``true`` when the given year is a leap +year, ``false`` otherwise. If the year is null, the current year is tested. +The controller is simple: it gets the year from the request attributes, pass +it to the `is_leap_year()`` function, and according to the return value it +creates a new Response object. + +As always, you can decide to stop here and use the framework as is; it's +probably all you need to create simple websites like those fancy one-page +`websites`_ and hopefully a few others. + +.. _`callbacks`: http://php.net/callback#language.types.callback +.. _`websites`: http://kottke.org/08/02/single-serving-sites From 0ce2a839aa6298dff7d573aec578d6b40df1e61d Mon Sep 17 00:00:00 2001 From: jdreesen Date: Wed, 11 Jan 2012 16:19:58 +0100 Subject: [PATCH 0023/2667] fixed some typos --- book/part5.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/book/part5.rst b/book/part5.rst index 088f4402171..f657ceb5901 100644 --- a/book/part5.rst +++ b/book/part5.rst @@ -9,8 +9,8 @@ idea, especially if you still have the separation of concerns principle in mind. Let's separate the template code from the logic by adding a new layer: the -controller: *The controller mission is to generate a Response based on the -information conveyed by the client Request.* +controller: *The controller's mission is to generate a Response based on the +information conveyed by the client's Request.* Change the template rendering part of the framework to read as follows:: @@ -142,7 +142,7 @@ Here is the updated and improved version of our framework:: To celebrate the birth of our new framework, let's create a brand new application that needs some simple logic. Our application has one page that says whether a given year is a leap year or not. When calling -``/is_leap_year``, you get the answer for the current year, but the you can +``/is_leap_year``, you get the answer for the current year, but you can also specify a year like in ``/is_leap_year/2009``. Being generic, the framework does not need to be modified in any way, just create a new ``app.php`` file:: From fdb195c18ba1f8d209ab9e1f35bb1667ce406d48 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 13 Jan 2012 12:08:22 +0100 Subject: [PATCH 0024/2667] added part 6 --- book/part6.rst | 205 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 book/part6.rst diff --git a/book/part6.rst b/book/part6.rst new file mode 100644 index 00000000000..d70c31ddc20 --- /dev/null +++ b/book/part6.rst @@ -0,0 +1,205 @@ +Create your own framework... on top of the Symfony2 Components (part 6) +======================================================================= + +You might think that our framework is already pretty solid and you are +probably right. But let's see how we can improve it nonetheless. + +Right now, all our examples use procedural code, but remember that controllers +can be any valid PHP callbacks. Let's convert our controller to a proper +class:: + + class LeapYearController + { + public function indexAction($request) + { + if (is_leap_year($request->attributes->get('year'))) { + return new Response('Yep, this is a leap year!'); + } + + return new Response('Nope, this is not a leap year.'); + } + } + +Update the route definition accordingly:: + + $routes->add('leap_year', new Routing\Route('/is_leap_year/{year}', array( + 'year' => null, + '_controller' => array(new LeapYearController(), 'indexAction'), + ))); + +The move is pretty straightforward and makes a lot of sense as soon as you +create more pages but you might have noticed a non-desirable side-effect... +The ``LeapYearController`` class is *always* instantiated, even if the +requested URL does not match the ``leap_year`` route. This is bad for one main +reason: performance wise, all controllers for all routes must now be +instantiated for every request. It would be better if controllers were +lazy-loaded so tha 8000 t only the controller associated with the matched route is +instantiated. + +To solve this issue, and a bunch more, let's install and use the HttpKernel +component:: + + { + "require": { + "symfony/class-loader": "2.1.*", + "symfony/http-foundation": "2.1.*", + "symfony/routing": "2.1.*", + "symfony/http-kernel": "2.1.*" + } + } + +The HttpKernel component has many interesting features, but the one we need +right now is the *controller resolver*. A controller resolver knows how to +determine the controller to execute and the arguments to pass to it, based on +a Request object. All controller resolvers implement the following interface:: + + namespace Symfony\Component\HttpKernel\Controller; + + interface ControllerResolverInterface + { + function getController(Request $request); + + function getArguments(Request $request, $controller); + } + +The ``getController()`` method relies on the same convention as the one we +have defined earlier: the ``_controller`` request attribute must contain the +controller associated with the Request. Besides the built-in PHP callbacks, +``getController()`` also supports strings composed of a class name followed by +two colons and a method name as a valid callback, like 'class::method':: + + $routes->add('leap_year', new Routing\Route('/is_leap_year/{year}', array( + 'year' => null, + '_controller' => 'LeapYearController::indexAction', + ))); + +To make this code work, modify the framework code to use the controller +resolver from HttpKernel:: + + use Symfony\Component\HttpKernel; + + $resolver = new HttpKernel\Controller\ControllerResolver(); + + $controller = $resolver->getController($request); + $arguments = $resolver->getArguments($request, $controller); + + $response = call_user_func_array($controller, $arguments); + +.. note:: + + As an added bonus, the controller resolver properly handles the error + management for you: when you forget to define a ``_controller`` attribute + for a Route for instance. + +Now, let's see how the controller arguments are guessed. ``getArguments()`` +introspects the controller signature to determine which arguments to pass to +it by using the native PHP `reflection`_. + +The ``indexAction()`` method needs the Request object as an argument. +```getArguments()`` knows when to inject it properly if it is type-hinted +correctly:: + + public function indexAction(Request $request) + + // won't work + public function indexAction($request) + +More interesting, ``getArguments()`` is also able to inject any Request +attribute; the argument just needs to have the same name as the corresponding +attribute:: + + public function indexAction($year) + +You can also inject the Request and some attributes at the same time (as the +matching is done on the argument name or a type hint, the arguments order does +not matter):: + + public function indexAction(Request $request, $year) + + public function indexAction($year, Request $request) + +Finally, you can also define default values for any argument that matches an +optional attribute of the Request:: + + public function indexAction($year = 2012) + +Let's just inject the ``$year`` request attribute for our controller:: + + class LeapYearController + { + public function indexAction($year) + { + if (is_leap_year($year)) { + return new Response('Yep, this is a leap year!'); + } + + return new Response('Nope, this is not a leap year.'); + } + } + +The controller resolver also takes care of validating the controller callable +and its arguments. In case of a problem, it throws an exception with a nice +message explaining the problem (the controller class does not exist, the +method is not defined, an argument has no matching attribute, ...). + +.. note:: + + With the great flexibility of the default controller resolver, you might + wonder why someone would want to create another one (why would there be an + interface if not). Two examples: in Symfony2, ``getController()`` is + enhanced to support `controllers as services`_; and in + `FrameworkExtraBundle`_, ``getArguments()`` is enhanced to support + parameter converters, where request attributes are converted to objects + automatically. + +Let's conclude with the new version of our framework:: + + attributes->all()); + ob_start(); + include sprintf(__DIR__.'/../src/pages/%s.php', $_route); + + return new Response(ob_get_clean()); + } + + $request = Request::createFromGlobals(); + $routes = include __DIR__.'/../src/app.php'; + + $context = new Routing\RequestContext(); + $context->fromRequest($request); + $matcher = new Routing\Matcher\UrlMatcher($routes, $context); + $resolver = new HttpKernel\Controller\ControllerResolver(); + + try { + $request->attributes->add($matcher->match($request->getPathInfo())); + + $controller = $resolver->getController($request); + $arguments = $resolver->getArguments($request, $controller); + + $response = call_user_func_array($controller, $arguments); + } catch (Routing\Exception\ResourceNotFoundException $e) { + $response = new Response('Not Found', 404); + } catch (Exception $e) { + $response = new Response('An error occurred', 500); + } + + $response->send(); + +Think about it once more: our framework is more robust and more flexible than +ever and it still has less than 40 lines of code. + +.. _`reflection`: http://php.net/reflection +.. _`FrameworkExtraBundle`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html +.. _`controllers as services`: http://symfony.com/doc/current/cookbook/controller/service.html From 97743fb879f1868965228cd3823a48175e5a5fec Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 15 Jan 2012 10:50:03 +0100 Subject: [PATCH 0025/2667] added part 7 --- book/part7.rst | 188 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 book/part7.rst diff --git a/book/part7.rst b/book/part7.rst new file mode 100644 index 00000000000..7679675e9eb --- /dev/null +++ b/book/part7.rst @@ -0,0 +1,188 @@ +Create your own framework... on top of the Symfony2 Components (part 7) +======================================================================= + +One down-side of our framework right now is that we need to copy and paste the +code in ``front.php`` each time we create a new website. 40 lines of code is +not that much, but it would be nice if we could wrap this code into a proper +class. It would bring us better *reusability* and easier testing to name just +a few benefits. + +If you have a closer look at the code, ``front.php`` has one input, the +Request, and one output, the Response. Our framework class will follow this +simple principle: the logic is about creating the Response associated with a +Request. + +As the Symfony2 components requires PHP 5.3, let's create our very own +namespace for our framework: ``Simplex``. + +Move the request handling logic into its own ``Simple\\Framework`` class:: + + matcher = $matcher; + $this->resolver = $resolver; + } + + public function handle(Request $request) + { + try { + $request->attributes->add($this->matcher->match($request->getPathInfo())); + + $controller = $this->resolver->getController($request); + $arguments = $this->resolver->getArguments($request, $controller); + + return call_user_func_array($controller, $arguments); + } catch (Routing\Exception\ResourceNotFoundException $e) { + return new Response('Not Found', 404); + } catch (Exception $e) { + return new Response('An error occurred', 500); + } + } + } + +And update ``example.com/web/front.php`` accordingly:: + + fromRequest($request); + $matcher = new Routing\Matcher\UrlMatcher($routes, $context); + $resolver = new HttpKernel\Controller\ControllerResolver(); + + $framework = new Simplex\Framework($matcher, $resolver); + $response = $framework->handle($request); + + $response->send(); + +To wrap up the refactoring, let's move everything but routes definition from +``example.com/src/app.php`` into yet another namespace: ``Calendar``. + +For the classes defined under the ``Simplex`` and ``Calendar`` namespaces to +be autoloaded, update the ``composer.json`` file: + +.. code-block:: json + + { + "require": { + "symfony/class-loader": "2.1.*", + "symfony/http-foundation": "2.1.*", + "symfony/routing": "2.1.*", + "symfony/http-kernel": "2.1.*" + }, + "autoload": { + "psr-0": { "Simplex": "src/", "Calendar": "src/" } + } + } + +.. note:: + + For the autoloader to be updated, run ``php composer.phar update``. + +Move the controller to ``Calendar\\Controller\\LeapYearController``:: + + isLeapYear($year)) { + return new Response('Yep, this is a leap year!'); + } + + return new Response('Nope, this is not a leap year.'); + } + } + +And move the ``is_leap_year()`` function to its own class too:: + + add('leap_year', new Routing\Route('/is_leap_year/{year}', array( + 'year' => null, + '_controller' => 'Calendar\\Controller\\LeapYearController::indexAction', + ))); + +To sum up, here is the new file layout: + + example.com + ├── composer.json + │ src + │ ├── app.php + │ └── Simplex + │ └── Framework.php + │ └── Calendar + │ └── Controller + │ │ └── LeapYearController.php + │ └── Model + │ └── LeapYear.php + ├── vendor + └── web + └── front.php + +That's it! Our application has now four different layers and each of them has +a well defined goal: + +* ``web/front.php``: The front controller; the only exposed PHP code that + makes the interface with the client (it gets the Request and sends the + Response) and provides the boiler-plate code to initialize the framework and + our application; + +* ``src/Simplex``: The reusable framework code that abstracts the handling of + incoming Requests (by the way, it makes your controllers/templates easily + testable -- more about that later on); + +* ``src/Calendar``: Our application specific code (the controllers and the + model); + +* ``src/app.php``: The application configuration/framework customization. From 9ffd1863c18d89af957a6b2adec9050021a7dbed Mon Sep 17 00:00:00 2001 From: gnugat Date: Sun, 15 Jan 2012 10:41:07 +0000 Subject: [PATCH 0026/2667] [part7] Fixing typo: adding 'x' to Simple/Framework class --- book/part7.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part7.rst b/book/part7.rst index 7679675e9eb..1c13da63884 100644 --- a/book/part7.rst +++ b/book/part7.rst @@ -15,7 +15,7 @@ Request. As the Symfony2 components requires PHP 5.3, let's create our very own namespace for our framework: ``Simplex``. -Move the request handling logic into its own ``Simple\\Framework`` class:: +Move the request handling logic into its own ``Simplex\\Framework`` class:: Date: Sun, 15 Jan 2012 17:26:32 +0100 Subject: [PATCH 0027/2667] fixed typos --- book/part7.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/book/part7.rst b/book/part7.rst index 1c13da63884..979b2fa4fe9 100644 --- a/book/part7.rst +++ b/book/part7.rst @@ -26,6 +26,7 @@ Move the request handling logic into its own ``Simplex\\Framework`` class:: use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Matcher\UrlMatcher; + use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\HttpKernel\Controller\ControllerResolver; class Framework @@ -48,9 +49,9 @@ Move the request handling logic into its own ``Simplex\\Framework`` class:: $arguments = $this->resolver->getArguments($request, $controller); return call_user_func_array($controller, $arguments); - } catch (Routing\Exception\ResourceNotFoundException $e) { + } catch (ResourceNotFoundException $e) { return new Response('Not Found', 404); - } catch (Exception $e) { + } catch (\Exception $e) { return new Response('An error occurred', 500); } } From 5674395eb95d3b1d180b2580ed27585822413211 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 15 Jan 2012 20:05:10 +0100 Subject: [PATCH 0028/2667] renamed part as we now know that we are going to have more than 10 parts --- book/{part1.rst => part01.rst} | 0 book/{part2.rst => part02.rst} | 0 book/{part3.rst => part03.rst} | 0 book/{part4.rst => part04.rst} | 0 book/{part5.rst => part05.rst} | 0 book/{part6.rst => part06.rst} | 0 book/{part7.rst => part07.rst} | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename book/{part1.rst => part01.rst} (100%) rename book/{part2.rst => part02.rst} (100%) rename book/{part3.rst => part03.rst} (100%) rename book/{part4.rst => part04.rst} (100%) rename book/{part5.rst => part05.rst} (100%) rename book/{part6.rst => part06.rst} (100%) rename book/{part7.rst => part07.rst} (100%) diff --git a/book/part1.rst b/book/part01.rst similarity index 100% rename from book/part1.rst rename to book/part01.rst diff --git a/book/part2.rst b/book/part02.rst similarity index 100% rename from book/part2.rst rename to book/part02.rst diff --git a/book/part3.rst b/book/part03.rst similarity index 100% rename from book/part3.rst rename to book/part03.rst diff --git a/book/part4.rst b/book/part04.rst similarity index 100% rename from book/part4.rst rename to book/part04.rst diff --git a/book/part5.rst b/book/part05.rst similarity index 100% rename from book/part5.rst rename to book/part05.rst diff --git a/book/part6.rst b/book/part06.rst similarity index 100% rename from book/part6.rst rename to book/part06.rst diff --git a/book/part7.rst b/book/part07.rst similarity index 100% rename from book/part7.rst rename to book/part07.rst From f00401d48e4f657d842052f6adbf00e7c037691d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 17 Jan 2012 10:42:31 +0100 Subject: [PATCH 0029/2667] added part 8 --- book/part08.rst | 189 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 book/part08.rst diff --git a/book/part08.rst b/book/part08.rst new file mode 100644 index 00000000000..d47b9217b3c --- /dev/null +++ b/book/part08.rst @@ -0,0 +1,189 @@ +Create your own framework... on top of the Symfony2 Components (part 8) +======================================================================= + +Some watchful readers pointed out some subtle but nonetheless important bugs +in the framework we have built yesterday. When creating a framework, you must +be sure that it behaves as advertised. If not, all the applications based on +it will exhibit the same bugs. The good news is that whenever you fix a bug, +you are fixing a bunch of applications too. + +Today's mission is to write unit tests for the framework we have created by +using `PHPUnit`_. Create a PHPUnit configuration file in +``example.com/phpunit.xml.dist``: + +.. code-block:: xml + + + + + + + ./tests + + + + +This configuration defines sensible defaults for most PHPUnit settings; more +interesting, the autoloader is used to bootstrap the tests, and tests will be +stored under the ``example.com/tests/`` directory. + +Now, let's write a test for "not found" resources. To avoid the creation of +all dependencies when writing tests and to really just unit-test what we want, +we are going to use `test doubles`_. Test doubles are easier to create when we +rely on interfaces instead of concrete classes. Fortunately, Symfony2 provides +such interfaces for core objects like the URL matcher and the controller +resolver. Modify the framework to make use of them:: + + matcher = $matcher; + $this->resolver = $resolver; + } + + // ... + } + +We are now ready to write our first test:: + + getFrameworkForException(new ResourceNotFoundException()); + + $response = $framework->handle(new Request()); + + $this->assertEquals(404, $response->getStatusCode()); + } + + protected function getFrameworkForException($exception) + { + $matcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface'); + $matcher + ->expects($this->once()) + ->method('match') + ->will($this->throwException($exception)) + ; + $resolver = $this->getMock('Symfony\Component\HttpKernel\Controller\ControllerResolverInterface'); + + return new Framework($matcher, $resolver); + } + } + +This test simulates a request that does not match any route. As such, the +``match()`` method returns a ``ResourceNotFoundException`` exception and we +are testing that our framework converts this exception to a 404 response. + +Executing this test is as simple as running ``phpunit`` from the +``example.com`` directory: + +.. code-block:: bash + + $ phpunit + +After the test ran, you should see a green bar. If not, you have a bug +either in the test or in the framework code! + +Adding a unit test for any exception thrown in a controller is just as easy:: + + public function testErrorHandling() + { + $framework = $this->getFrameworkForException(new \RuntimeException()); + + $response = $framework->handle(new Request()); + + $this->assertEquals(500, $response->getStatusCode()); + } + +Last, but not the least, let's write a test for when we actually have a proper +Response:: + + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\HttpKernel\Controller\ControllerResolver; + + public function testControllerResponse() + { + $matcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface'); + $matcher + ->expects($this->once()) + ->method('match') + ->will($this->returnValue(array( + '_route' => 'foo', + 'name' => 'Fabien', + '_controller' => function ($name) { + return new Response('Hello '.$name); + } + ))) + ; + $resolver = new ControllerResolver(); + + $framework = new Framework($matcher, $resolver); + + $response = $framework->handle(new Request()); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertContains('Hello Fabien', $response->getContent()); + } + +In this test, we simulate a route that matches and returns a simple +controller. We check that the response status is 200 and that its content is +the one we have set in the controller. + +To check that we have covered all possible use cases, run the PHPUnit test +coverage feature (you need to enable `XDebug`_ first): + +.. code-block:: bash + + phpunit --coverage-html=cov/ + +Open ``example.com/cov/src_Simplex_Framework.php.html`` in a browser and check +that all the lines for the Framework class are green (it means that they have +been visited when the tests were executed). + +Thanks to the simple object-oriented code that we have written so far, we have +been able to write unit-tests to cover all possible use cases of our +framework; test doubles ensured that we were actually testing our code and not +Symfony2 code. + +Now that we are confident (again) about the code we have written, we can +safely think about the next batch of features we want to add to our framework. + +.. _`PHPUnit`: http://www.phpunit.de/manual/current/en/index.html +.. _`test doubles`: http://www.phpunit.de/manual/current/en/test-doubles.html +.. _`XDebug`: http://xdebug.org/ From 54e1b0829c0f669134b05a7ba7a4586f112c6cef Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 17 Jan 2012 10:47:17 +0100 Subject: [PATCH 0030/2667] added a note in part 8 --- book/part08.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/book/part08.rst b/book/part08.rst index d47b9217b3c..6e50b557ca7 100644 --- a/book/part08.rst +++ b/book/part08.rst @@ -117,6 +117,12 @@ Executing this test is as simple as running ``phpunit`` from the $ phpunit +.. note:: + + I do not explain how the code works in details as this is not the goal of + this series, but if you don't understand what the hell is going on, I + highly recommend you to read PHPUnit documentation on `test doubles`_. + After the test ran, you should see a green bar. If not, you have a bug either in the test or in the framework code! @@ -170,7 +176,7 @@ coverage feature (you need to enable `XDebug`_ first): .. code-block:: bash - phpunit --coverage-html=cov/ + $ phpunit --coverage-html=cov/ Open ``example.com/cov/src_Simplex_Framework.php.html`` in a browser and check that all the lines for the Framework class are green (it means that they have From bde64c898b3c627c3ac9bde02a14ce2f7e0fb5fd Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 19 Jan 2012 17:25:16 +0100 Subject: [PATCH 0031/2667] made small tweaks --- book/part09.rst | 329 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 book/part09.rst diff --git a/book/part09.rst b/book/part09.rst new file mode 100644 index 00000000000..12652f1498f --- /dev/null +++ b/book/part09.rst @@ -0,0 +1,329 @@ +Create your own framework... on top of the Symfony2 Components (part 9) +======================================================================= + +Our framework is still missing a major characteristic of any good framework: +*extensibility*. Being extensible means that the developer should be able to +easily hook into the framework life cycle to modify the way the request is +handled. + +What kind of hooks are we talking about? Authentication or caching for +instance. To be flexible, hooks must be plug-and-play; the ones you "register" +for an application are different from the next one depending on your specific +needs. Many software have a similar concept like Drupal or Wordpress. In some +languages, there is even a standard like `WSGI`_ in Python or `Rack`_ in Ruby. + +As there is no standard for PHP, we are going to use a well-known design +pattern, the *Observer*, to allow any kind of behaviors to be attached to our +framework; the Symfony2 EventDispatcher Component implements a lightweight +version of this pattern: + +.. code-block:: json + + { + "require": { + "symfony/class-loader": "2.1.*", + "symfony/http-foundation": "2.1.*", + "symfony/routing": "2.1.*", + "symfony/http-kernel": "2.1.*", + "symfony/event-dispatcher": "2.1.*" + }, + "autoload": { + "psr-0": { "Simplex": "src/", "Calendar": "src/" } + } + } + +How does it work? The *dispatcher*, the central object of the event dispatcher +system, notifies *listeners* of an *event* dispatched to it. Put another way: +your code dispatches an event to the dispatcher, the dispatcher notifies all +registered listeners for the event, and each listener do whatever it wants +with the event. + +As an example, let's create a listener that transparently adds the Google +Analytics code to all responses. + +To make it work, the framework must dispatch an event just before returning +the Response instance:: + + matcher = $matcher; + $this->resolver = $resolver; + $this->dispatcher = $dispatcher; + } + + public function handle(Request $request) + { + try { + $request->attributes->add($this->matcher->match($request->getPathInfo())); + + $controller = $this->resolver->getController($request); + $arguments = $this->resolver->getArguments($request, $controller); + + $response = call_user_func_array($controller, $arguments); + } catch (ResourceNotFoundException $e) { + $response = new Response('Not Found', 404); + } catch (\Exception $e) { + $response = new Response('An error occurred', 500); + } + + // dispatch a response event + $this->dispatcher->dispatch('response', new ResponseEvent($response, $request)); + + return $response; + } + } + +Each time the framework handles a Request, a ``ResponseEvent`` event is +now dispatched:: + + response = $response; + $this->request = $request; + } + + public function getResponse() + { + return $this->response; + } + + public function getRequest() + { + return $this->request; + } + } + +The last step is the creation of the dispatcher in the front controller and +the registration of a listener for the ``response`` event:: + + addListener('response', function (Simplex\ResponseEvent $event) { + $response = $event->getResponse(); + + if ($response->isRedirection() + || ($response->headers->has('Content-Type') && false === strpos($response->headers->get('Content-Type'), 'html')) + || 'html' !== $event->getRequest()->getRequestFormat() + ) { + return; + } + + $response->setContent($response->getContent().'GA CODE'); + }); + + $framework = new Simplex\Framework($dispatcher, $matcher, $resolver); + $response = $framework->handle($request); + + $response->send(); + +.. note:: + + The listener is just a proof of concept and you should add the Google + Analytics code just before the body tag. + +As you can see, ``addListener()`` associates a valid PHP callback to a named +event (``response``); the event name must be the same as the one used in the +``dispatch()`` call. + +In the listener, we add the Google Analytics code only if the response is not +a redirection, if the requested format is HTML, and if the response content +type is HTML (these conditions demonstrate the ease of manipulating the +Request and Response data from your code). + +So far so good, but let's add another listener on the same event. Let's say +that I want to set the ``Content-Length`` of the Response if it is not already +set:: + + $dispatcher->addListener('response', function (Simplex\ResponseEvent $event) { + $response = $event->getResponse(); + $headers = $response->headers; + + if (!$headers->has('Content-Length') && !$headers->has('Transfer-Encoding')) { + $headers->set('Content-Length', strlen($response->getContent())); + } + }); + +Depending on whether you have added this piece of code before the previous +listener registration or after it, you will have the wrong or the right value +for the ``Content-Length`` header. Sometimes, the order of the listeners +matter but by default, all listeners are 8000 registered with the same priority, +``0``. To tell the dispatcher to run a listener early, change the priority to +a positive number; negative numbers can be used for low priority listeners. +Here, we want the ``Content-Length`` listener to be executed last, so change +the priority to ``-255``:: + + $dispatcher->addListener('response', function (Simplex\ResponseEvent $event) { + $response = $event->getResponse(); + $headers = $response->headers; + + if (!$headers->has('Content-Length') && !$headers->has('Transfer-Encoding')) { + $headers->set('Content-Length', strlen($response->getContent())); + } + }, -255); + +.. tip:: + + When creating your framework, think about priorities (reserve some numbers + for internal listeners for instance) and document them thoroughly. + +Let's refactor the code a bit by moving the Google listener to its own class:: + + getResponse(); + + if ($response->isRedirection() + || ($response->headers->has('Content-Type') && false === strpos($response->headers->get('Content-Type'), 'html')) + || 'html' !== $event->getRequest()->getRequestFormat() + ) { + return; + } + + $response->setContent($response->getContent().'GA CODE'); + } + } + +And do the same with the other listener:: + + getResponse(); + $headers = $response->headers; + + if (!$headers->has('Content-Length') && !$headers->has('Transfer-Encoding')) { + $headers->set('Content-Length', strlen($response->getContent())); + } + } + } + +Our front controller should now look like the following:: + + $dispatcher = new EventDispatcher(); + $dispatcher->addListener('response', array(new Simplex\ContentLengthListener(), 'onResponse'), -255); + $dispatcher->addListener('response', array(new Simplex\GoogleListener(), 'onResponse')); + +Even if the code is now nicely wrapped in classes, there is still a slight +issue: the knowledge of the priorities is "hardcoded" in the front controller, +instead of being in the listeners themselves. For each application, you have +to remember to set the appropriate priorities. Moreover, the listener method +names are also exposed here, which means that refactoring our listeners would +mean changing all the applications that rely on those listeners. Of course, +there is a solution: use subscribers instead of listeners:: + + $dispatcher = new EventDispatcher(); + $dispatcher->addSubscriber(new Simplex\ContentLengthListener()); + $dispatcher->addSubscriber(new Simplex\GoogleListener()); + +A subscriber knowns about all the events it is interested in and pass this +information to the dispatcher via the ``getSubscribedEvents()`` method. Have a +look at the new version of the ``GoogleListener``:: + + 'onResponse'); + } + } + +And here is the new version of ``ContentLengthListener``:: + + array('onResponse', -255)); + } + } + +.. tip:: + + A single subscriber can host as many listeners as you want on as many + events as needed. + +To make your framework truly flexible, don't hesitate to add more events; and +to make it more awesome out of the box, add more listeners. Again, this series +is not about creating a generic framework, but one that is tailored to your +needs. Stop whenever you see fit, and further evolve the code from there. + +.. _`WSGI`: http://www.python.org/dev/peps/pep-0333/#middleware-components-that-play-both-sides +.. _`Rack`: http://rack.rubyforge.org/ From a635d89d285588204550d02059260bba906e20e8 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 21 Jan 2012 08:43:01 +0100 Subject: [PATCH 0032/2667] added part 10 --- book/part10.rst | 193 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 book/part10.rst diff --git a/book/part10.rst b/book/part10.rst new file mode 100644 index 00000000000..3f3cc14e37f --- /dev/null +++ b/book/part10.rst @@ -0,0 +1,193 @@ +Create your own framework... on top of the Symfony2 Components (part 10) +======================================================================== + +In the conclusion of the second part of this series, I've talked about one +great benefit of using the Symfony2 components: the *interoperability* between +all frameworks and applications using them. Let's do a big step towards this +goal by making our framework implement ``HttpKernelInterface``:: + + namespace Symfony\Component\HttpKernel; + + interface HttpKernelInterface + { + /** + * @return Response A Response instance + */ + function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true); + } + +``HttpKernelInterface`` is probably the most important piece of code in the +HttpKernel component, no kidding. Frameworks and applications that implement +this interface are fully interoperable. Moreover, a lot of great features will +come with it for free. + +Update your framework so that it implements this interface:: + + handle($request)->send(); + +That's all it takes to add HTTP caching support to our framework. Isn't it +amazing? + +Configuring the cache needs to be done via HTTP cache headers. For instance, +to cache a response for 10 seconds, use the ``Response::setTtl()`` method:: + + // example.com/src/Calendar/Controller/LeapYearController.php + + public function indexAction(Request $request, $year) + { + $leapyear = new LeapYear(); + if ($leapyear->isLeapYear($year)) { + $response = new Response('Yep, this is a leap year!'); + } else { + $response = new Response('Nope, this is not a leap year.'); + } + + $response->setTtl(10); + + return $response; + } + +.. tip:: + + If, like me, you are running your framework from the command line by + simulating requests (``Request::create('/is_leap_year/2012')``), you can + easily debug Response instances by dumping their string representation + (``echo $response;``) as it displays all headers as well as the response + content. + +To validate that it works correctly, add a random number to the response +content and check that the number only changes every 10 seconds:: + + $response = new Response('Yep, this is a leap year! '.rand()); + +.. note:: + + When deploying to your production environment, keep using the Symfony2 + reverse proxy (great for shared hosting) or even better, switch to a more + efficient reverse proxy like `Varnish`_. + +Using HTTP cache headers to manage your application cache is very powerful and +allows you to finely tuned your caching strategy as you can use both the +expiration and the validation models of the HTTP specification. If you are not +comfortable with these concepts, I highly recommend you to read the `HTTP +caching`_ chapter of the Symfony2 documentation. + +The Response class contains many other methods that let's you configure the +HTTP cache very easily. One of the most powerful is ``setCache()`` as it +abstracts the most frequently used caching strategies into one simple array:: + + $date = date_create_from_format('Y-m-d H:i:s', '2005-10-15 10:00:00'); + + $response->setCache(array( + 'public' => true, + 'etag' => 'abcde', + 'last_modified' => $date, + 'max_age' => 10, + 's_maxage' => 10, + )); + + // it is equivalent to the following code + $response->setPublic(); + $response->setEtag('abcde'); + $response->setLastModified($date); + $response->setMaxAge(10); + $response->setSharedMaxAge(10); + +When using the validation model, the ``isNotModified()`` method allows you to +easily cut on the response time by short-circuiting the response generation as +early as possible:: + + $response->setETag('whatever_you_compute_as_an_etag'); + + if ($response->isNotModified($request)) { + return $response; + } + $response->setContent('The computed content of the response'); + + return $response; + +Using HTTP caching is great, but what if you cannot cache the whole page? What +if you can cache everything but some sidebar that is more dynamic that the +rest of the content? Edge Side Includes (`ESI`_) to the rescue! Instead of +generating the whole content in one go, ESI allows you to mark a region of a +page as being the content of a sub-request call:: + + This is the content of your page + + Is 2012 a leap year? + + Some other content + +For ESI tags to be supported by HttpCache, you need to pass it an instance of +the ``ESI`` class. The ``ESI`` class automatically parses ESI tags and makes +sub-requests to convert them to their proper content:: + + use Symfony\Component\HttpKernel\HttpCache\ESI; + + $framework = new HttpCache($framework, new Store(__DIR__.'/../cache'), new ESI()); + +.. note:: + + For ESI to work, you need to use a reverse proxy that supports it like the + Symfony2 implementation. `Varnish`_ is the best alternative and it is + Open-Source. + +When using complex HTTP caching strategies and/or many ESI include tags, it +can be hard to understand why and when a resource should be cached or not. To +ease debugging, you can enable the debug mode:: + + $framework = new HttpCache($framework, new Store(__DIR__.'/../cache'), new ESI(), array('debug' => true)); + +The debug mode adds a ``X-Symfony-Cache`` header to each response that +describes what the cache layer did: + +.. code-block:: text + + X-Symfony-Cache: GET /is_leap_year/2012: stale, invalid, store + + X-Symfony-Cache: GET /is_leap_year/2012: fresh + +HttpCache has many some features like support for the +``stale-while-revalidate`` and ``stale-if-error`` HTTP Cache-Control +extensions as defined in RFC 5861. + +With the addition of a single interface, our framework can now benefit from +the many features built into the HttpKernel component; HTTP caching being just +one of them but an important one as it can make your applications fly! + +.. _`HTTP caching`: http://symfony.com/doc/current/book/http_cache.html +.. _`ESI`: http://en.wikipedia.org/wiki/Edge_Side_Includes +.. _`Varnish`: https://www.varnish-cache.org/ From 111cac05d43e9e9dc09de682b6cebaaff51f09b9 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 21 Jan 2012 10:02:33 +0100 Subject: [PATCH 0033/2667] removed some use statement to be more consistent with previous parts --- book/part10.rst | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/book/part10.rst b/book/part10.rst index 3f3cc14e37f..f6f9ff6d25e 100644 --- a/book/part10.rst +++ b/book/part10.rst @@ -50,11 +50,8 @@ PHP; it implements ``HttpKernelInterface`` and wraps another // example.com/web/front.php - use Symfony\Component\HttpKernel\HttpCache\HttpCache; - use Symfony\Component\HttpKernel\HttpCache\Store; - $framework = new Simplex\Framework($dispatcher, $matcher, $resolver); - $framework = new HttpCache($framework, new Store(__DIR__.'/../cache')); + $framework = new HttpKernel\HttpCache\HttpCache($framework, new HttpKernel\HttpCache\Store(__DIR__.'/../cache')); $framework->handle($request)->send(); @@ -155,9 +152,11 @@ For ESI tags to be supported by HttpCache, you need to pass it an instance of the ``ESI`` class. The ``ESI`` class automatically parses ESI tags and makes sub-requests to convert them to their proper content:: - use Symfony\Component\HttpKernel\HttpCache\ESI; - - $framework = new HttpCache($framework, new Store(__DIR__.'/../cache'), new ESI()); + $framework = new HttpKernel\HttpCache\HttpCache( + $framework, + new HttpKernel\HttpCache\Store(__DIR__.'/../cache'), + new HttpKernel\HttpCache\ESI() + ); .. note:: From 8399581800e9fb85a387e7da1ea3d04e5367cc18 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 21 Jan 2012 15:24:55 +0100 Subject: [PATCH 0034/2667] moved the Context::fromRequest() code to the Framework class --- book/part07.rst | 3 ++- book/part09.rst | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/book/part07.rst b/book/part07.rst index 979b2fa4fe9..605d84e6f8a 100644 --- a/book/part07.rst +++ b/book/part07.rst @@ -42,6 +42,8 @@ Move the request handling logic into its own ``Simplex\\Framework`` class:: public function handle(Request $request) { + $this->matcher->getContext()->fromRequest($request); + try { $request->attributes->add($this->matcher->match($request->getPathInfo())); @@ -69,7 +71,6 @@ And update ``example.com/web/front.php`` accordingly:: $routes = include __DIR__.'/../src/app.php'; $context = new Routing\RequestContext(); - $context->fromRequest($request); $matcher = new Routing\Matcher\UrlMatcher($routes, $context); $resolver = new HttpKernel\Controller\ControllerResolver(); diff --git a/book/part09.rst b/book/part09.rst index 12652f1498f..55674f2b204 100644 --- a/book/part09.rst +++ b/book/part09.rst @@ -72,6 +72,8 @@ the Response instance:: public function handle(Request $request) { + $this->matcher->getContext()->fromRequest($request); + try { $request->attributes->add($this->matcher->match($request->getPathInfo())); From 02aab54e7c26d84eef2c2020fb29940de39b9f92 Mon Sep 17 00:00:00 2001 From: Arnaud Kleinpeter Date: Sat, 21 Jan 2012 16:31:40 +0100 Subject: [PATCH 0035/2667] Corrected some english errors --- book/part10.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/book/part10.rst b/book/part10.rst index f6f9ff6d25e..7e32d77970a 100644 --- a/book/part10.rst +++ b/book/part10.rst @@ -97,12 +97,12 @@ content and check that the number only changes every 10 seconds:: efficient reverse proxy like `Varnish`_. Using HTTP cache headers to manage your application cache is very powerful and -allows you to finely tuned your caching strategy as you can use both the +allows you to tune finely your caching strategy as you can use both the expiration and the validation models of the HTTP specification. If you are not comfortable with these concepts, I highly recommend you to read the `HTTP caching`_ chapter of the Symfony2 documentation. -The Response class contains many other methods that let's you configure the +The Response class contains many other methods that let you configure the HTTP cache very easily. One of the most powerful is ``setCache()`` as it abstracts the most frequently used caching strategies into one simple array:: @@ -179,7 +179,7 @@ describes what the cache layer did: X-Symfony-Cache: GET /is_leap_year/2012: fresh -HttpCache has many some features like support for the +HttpCache has many features like support for the ``stale-while-revalidate`` and ``stale-if-error`` HTTP Cache-Control extensions as defined in RFC 5861. From fda9900e03620550a8eb5ec65cff94fe8d26dba3 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 22 Jan 2012 08:08:37 +0100 Subject: [PATCH 0036/2667] fixed pygments code name for json --- book/part01.rst | 2 +- book/part02.rst | 2 +- book/part07.rst | 2 +- book/part09.rst | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/book/part01.rst b/book/part01.rst index 8188a0e9afe..cebc1b98503 100644 --- a/book/part01.rst +++ b/book/part01.rst @@ -108,7 +108,7 @@ To install the Symfony2 Components that we need for our framework, we are going to use `Composer`_, a project dependency manager for PHP. First, list your dependencies in a ``composer.json`` file: -.. code-block:: json +.. code-block:: javascript { "require": { diff --git a/book/part02.rst b/book/part02.rst index 43632a17b84..f2bc1502025 100644 --- a/book/part02.rst +++ b/book/part02.rst @@ -127,7 +127,7 @@ layer. To use this component, open the ``composer.json`` file and add it as a dependency for the project: -.. code-block:: json +.. code-block:: javascript { "require": { diff --git a/book/part07.rst b/book/part07.rst index 605d84e6f8a..384cd12ed5c 100644 --- a/book/part07.rst +++ b/book/part07.rst @@ -85,7 +85,7 @@ To wrap up the refactoring, let's move everything but routes definition from For the classes defined under the ``Simplex`` and ``Calendar`` namespaces to be autoloaded, update the ``composer.json`` file: -.. code-block:: json +.. code-block:: javascript { "require": { diff --git a/book/part09.rst b/book/part09.rst index 55674f2b204..c35b871565a 100644 --- a/book/part09.rst +++ b/book/part09.rst @@ -17,7 +17,7 @@ pattern, the *Observer*, to allow any kind of behaviors to be attached to our framework; the Symfony2 EventDispatcher Component implements a lightweight version of this pattern: -.. code-block:: json +.. code-block:: javascript { "require": { From 76e45f99af29f7b8ef3149de9d88ba7a8a6825fe Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 22 Jan 2012 08:11:38 +0100 Subject: [PATCH 0037/2667] fixed markup --- book/part03.rst | 2 ++ book/part04.rst | 2 +- book/part07.rst | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/book/part03.rst b/book/part03.rst index 5ae29f79aeb..be9e5124fee 100644 --- a/book/part03.rst +++ b/book/part03.rst @@ -155,6 +155,8 @@ Now that the web server always access the same script (``front.php``) for all our pages, we can secure our code further by moving all other PHP files outside the web root directory: +.. code-block:: text + example.com ├── composer.json │ src diff --git a/book/part04.rst b/book/part04.rst index 056934204ce..6a168aeac89 100644 --- a/book/part04.rst +++ b/book/part04.rst @@ -55,7 +55,7 @@ instead of relying on a query string: To support this feature, we are going to use the Symfony2 Routing component. As always, add it to ``composer.json`` and run the ``php composer.phar -update`` command to install it:: +update`` command to install it: .. code-block:: json diff --git a/book/part07.rst b/book/part07.rst index 384cd12ed5c..964c897b403 100644 --- a/book/part07.rst +++ b/book/part07.rst @@ -157,6 +157,8 @@ Don't forget to update the ``example.com/src/app.php`` file accordingly:: To sum up, here is the new file layout: +.. code-block:: text + example.com ├── composer.json │ src From 09969d900f3a73de62678c47b69fcc0f95eb1abc Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 23 Jan 2012 14:31:16 +0100 Subject: [PATCH 0038/2667] added part 11 --- book/part11.rst | 212 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 book/part11.rst diff --git a/book/part11.rst b/book/part11.rst new file mode 100644 index 00000000000..4e581fc063e --- /dev/null +++ b/book/part11.rst @@ -0,0 +1,212 @@ +Create your own framework... on top of the Symfony2 Components (part 11) +======================================================================== + +If you were to use our framework right now, you would probably have to add +support for custom error messages. Right now, we have 404 and 500 error +support but the responses are hardcoded in the framework itself. Making them +customizable is easy enough though: dispatch a new event and listen to it. +Doing it right means that the listener have to call a regular controller. But +what if the error controller throws an exception? You will end up in an +infinite loop. There should be an easier way, right? + +Enter the ``HttpKernel`` class. Instead of solving the same problem over and +over again and instead of reinventing the wheel each time, the ``HttpKernel`` +class is a generic, extensible, and flexible implementation of +``HttpKernelInterface``. + +This class is very similar to the framework class we have written so far: it +dispatches events at some strategic points during the handling of the request, +it uses a controller resolver to choose the controller to dispatch the request +to, and as an added bonus, it takes care of edge cases and provides great +feedback when a problem arises. + +Here is the new framework code:: + + addSubscriber(new HttpKernel\EventListener\RouterListener($matcher)); + + $framework = new Simplex\Framework($dispatcher, $resolver); + + $response = $framework->handle($request); + $response->send(); + +``RouterListener`` is an implementation of the same logic we had in our +framework: it matches the incoming request and populates the request +attributes with route parameters. + +Our code is now much more concise and surprisingly more robust and more +powerful than ever. For instance, use the built-in ``ExceptionListener`` to +make your error management configurable:: + + $errorHandler = function (HttpKernel\Exception\FlattenException $exception) { + $msg = 'Something went wrong! ('.$exception->getMessage().')'; + + return new Response($msg, $exception->getStatusCode()); + }); + $dispatcher->addSubscriber(new HttpKernel\EventListener\ExceptionListener($errorHandler); + +``ExceptionListener`` gives you a ``FlattenException`` instance instead of the +thrown ``Exception`` instance to ease exception manipulation and display. It +can take any valid controller as an exception handler, so you can create an +ErrorController class instead of using a Closure:: + + $listener = new HttpKernel\EventListener\ExceptionListener('Calendar\\Controller\\ErrorController::exceptionAction'); + $dispatcher->addSubscriber($listener); + +The error controller reads as follows:: + + getMessage().')'; + + return new Response($msg, $exception->getStatusCode()); + } + } + +Voilà! Clean and customizable error management without efforts. And of course, +of your controller throws an exception, HttpKernel will handle it nicely. + +In part 2, we have talked about the ``Response::prepare()`` method, which +ensures that a Response is compliant with the HTTP specification. It is +probably a good idea to always call it just before sending the Response to the +client; that's what the ``ResponseListener`` does:: + + $dispatcher->addSubscriber(new HttpKernel\EventListener\ResponseListener('UTF-8')); + +This one was easy too! Let's take another one: do you want out of the box +support for streamed responses? Just subscribe to +``StreamedResponseListener``:: + + $dispatcher->addSubscriber(new HttpKernel\EventListener\StreamedResponseListener()); + +And in your controller, return a ``StreamedResponse`` instance instead of a +``Response`` instance. + +.. tip:: + + Read the `Internals`_ chapter of the Symfony2 documentation to learn more + about the events dispatched by HttpKernel and how they allow you to change + the flow of a request. + +Now, let's create a listener, one that allows a controller to return a string +instead of a full Response object:: + + class LeapYearController + { + public function indexAction(Request $request, $year) + { + $leapyear = new LeapYear(); + if ($leapyear->isLeapYear($year)) { + return 'Yep, this is a leap year! '; + } + + return 'Nope, this is not a leap year.'; + } + } + +To implement this feature, we are going to listen to the ``kernel.view`` +event, which is triggered just after the controller has been called. Its goal +is to convert the controller return value to a proper Response instance, but +only if needed:: + + getControllerResult(); + + if (is_string($response)) { + $event->setResponse(new Response($response)); + } + } + + public static function getSubscribedEvents() + { + return array('kernel.view' => 'onView'); + } + } + +The code is simple because the ``kernel.view`` event is only triggered when +the controller return value is not a Response and because setting the response +on the event stops the event propagation (our listener cannot interfere with +other view listeners). + +Don't forget to register it in the front controller:: + + $dispatcher->addSubscriber(new Simplex\StringResponseListener()); + +.. note:: + + If you forget to register the subscriber, HttpKernel will throws an + exception with a nice message: ``The controller must return a response + (Nope, this is not a leap year. given).``. + +At this point, our whole framework code is as compact as possible and it is +mainly composed of an assembling of existing libraries. Extending is a matter +of registering event listeners/subscribers. + +Hopefully, you now have a better understanding of why the simple looking +``HttpKernelInterface`` is so powerful. Its default implementation, +``HttpKernel``, gives you access to a lot of cool features, ready to be used +out of the box, with no efforts. And because HttpKernel is actually the code +that powers the Symfony2 and Silex frameworks, you have the best of both +worlds: a custom framework, tailored to your needs, but based on a rock-solid +and well maintained low-level architecture that has been proven to work for +many websites; a code that has been audited for security issues and that has +proven to scale well. + +.. _`Internals`: http://symfony.com/doc/current/book/internals.html#events From a3f0b3129c594d51c6a14dadfe2bb8fec0050ed4 Mon Sep 17 00:00:00 2001 From: Arnaud Kleinpeter Date: Mon, 23 Jan 2012 15:48:59 +0100 Subject: [PATCH 0039/2667] Corrected few typos --- book/part11.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/book/part11.rst b/book/part11.rst index 4e581fc063e..debd0fca5bc 100644 --- a/book/part11.rst +++ b/book/part11.rst @@ -5,7 +5,7 @@ If you were to use our framework right now, you would probably have to add support for custom error messages. Right now, we have 404 and 500 error support but the responses are hardcoded in the framework itself. Making them customizable is easy enough though: dispatch a new event and listen to it. -Doing it right means that the listener have to call a regular controller. But +Doing it right means that the listener has to call a regular controller. But what if the error controller throws an exception? You will end up in an infinite loop. There should be an easier way, right? @@ -108,7 +108,7 @@ The error controller reads as follows:: } Voilà! Clean and customizable error management without efforts. And of course, -of your controller throws an exception, HttpKernel will handle it nicely. +if your controller throws an exception, HttpKernel will handle it nicely. In part 2, we have talked about the ``Response::prepare()`` method, which ensures that a Response is compliant with the HTTP specification. It is @@ -191,12 +191,12 @@ Don't forget to register it in the front controller:: .. note:: - If you forget to register the subscriber, HttpKernel will throws an + If you forget to register the subscriber, HttpKernel will throw an exception with a nice message: ``The controller must return a response (Nope, this is not a leap year. given).``. At this point, our whole framework code is as compact as possible and it is -mainly composed of an assembling of existing libraries. Extending is a matter +mainly composed of an assembly of existing libraries. Extending is a matter of registering event listeners/subscribers. Hopefully, you now have a better understanding of why the simple looking From 16b5b09a27ac3da9746b89ee8e97de40e28172c0 Mon Sep 17 00:00:00 2001 From: William DURAND Date: Tue, 24 Jan 2012 15:25:37 +0100 Subject: [PATCH 0040/2667] Fixed typo --- book/part11.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part11.rst b/book/part11.rst index debd0fca5bc..1d55c653aac 100644 --- a/book/part11.rst +++ b/book/part11.rst @@ -76,7 +76,7 @@ make your error management configurable:: return new Response($msg, $exception->getStatusCode()); }); - $dispatcher->addSubscriber(new HttpKernel\EventListener\ExceptionListener($errorHandler); + $dispatcher->addSubscriber(new HttpKernel\EventListener\ExceptionListener($errorHandler)); ``ExceptionListener`` gives you a ``FlattenException`` instance instead of the thrown ``Exception`` instance to ease exception manipulation and display. It From 9bc692f1046a7b11decb2 8000 aca6c4fc1e31baffaf9 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 25 Jan 2012 07:44:02 +0100 Subject: [PATCH 0041/2667] added part 12 --- book/part12.rst | 256 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 book/part12.rst diff --git a/book/part12.rst b/book/part12.rst new file mode 100644 index 00000000000..10a9b34f35a --- /dev/null +++ b/book/part12.rst @@ -0,0 +1,256 @@ +Create your own framework... on top of the Symfony2 Components (part 12) +======================================================================== + +In the last installment of this series, we have emptied the +``Simplex\\Framework`` class by extending the ``HttpKernel`` class from +Symfony. Seeing this empty class, you might be tempted to move some code from +the front controller to it:: + + addSubscriber(new HttpKernel\EventListener\RouterListener($matcher)); + $dispatcher->addSubscriber(new HttpKernel\EventListener\ResponseListener('UTF-8')); + + parent::__construct($dispatcher, $resolver); + } + } + +The front controller code would become more concise:: + + handle($request)->send(); + +Having a more concise front controller means that you can have more than one +for a single application. Why would it be useful? To allow having different +configuration for the development environment and the production one for +instance. In the development environment, you might want to have error +reporting turned on and errors displayed in the browser to ease debugging:: + + ini_set('display_errors', 1); + error_reporting(-1); + +... but you certainly won't want that same configuration on the production +environment. Having two different front controllers gives you the opportunity +to have a slightly different configuration for each of them. + +So, moving code from the front controller to the framework class makes our +framework more configurable, but at the same time, it introduces a lot of +issues: + +* We are not able to register custom listeners anymore as the dispatcher is + not available outside the Framework class (an easy workaround could be the + adding of a ``Framework::getEventDispatcher()`` method); + +* We have lost the flexibility we had before; you cannot change the + implementation of the ``UrlMatcher`` or of the ``ControllerResolver`` + anymore; + +* Related to the previous point, we cannot test our framework easily anymore + as it's impossible to mock internal objects; + +* We cannot change the charset passed to ResponseListener anymore (a + workaround could be to pass it as a constructor argument). + +The previous code did not exhibit the same issues because we used dependency +injection; all dependencies of our objects were injected into their +constructors (for instance, the event dispatcher were injected into the +framework so that we had total control of its creation and configuration). + +Does it means that we have to make a choice between flexibility, +customization, ease of testing and not having to copy and paste the same code +into each application front controller? As you might expect, there is a +solution. We can solve all these issues and some more by using the Symfony2 +dependency injection container: + +.. code-block:: json + + { + "require": { + "symfony/class-loader": "2.1.*", + "symfony/http-foundation": "2.1.*", + "symfony/routing": "2.1.*", + "symfony/http-kernel": "2.1.*", + "symfony/event-dispatcher": "2.1.*", + "symfony/dependency-injection": "2.1.*" + }, + "autoload": { + "psr-0": { "Simplex": "src/", "Calendar": "src/" } + } + } + +Create a new file to host the dependency injection container configuration:: + + register('context', 'Symfony\Component\Routing\RequestContext'); + $sc->register('matcher', 'Symfony\Component\Routing\Matcher\UrlMatcher') + ->setArguments(array($routes, new Reference('context'))) + ; + $sc->register('resolver', 'Symfony\Component\HttpKernel\Controller\ControllerResolver'); + + $sc->register('listener.router', 'Symfony\Component\HttpKernel\EventListener\RouterListener') + ->setArguments(array(new Reference('matcher'))) + ; + $sc->register('listener.response', 'Symfony\Component\HttpKernel\EventListener\ResponseListener') + ->setArguments(array('UTF-8')) + ; + $sc->register('listener.exception', 'Symfony\Component\HttpKernel\EventListener\ExceptionListener') + ->setArguments(array('Calendar\\Controller\\ErrorController::exceptionAction')) + ; + $sc->register('dispatcher', 'Symfony\Component\EventDispatcher\EventDispatcher') + ->addMethodCall('addSubscriber', array(new Reference('listener.router'))) + ->addMethodCall('addSubscriber', array(new Reference('listener.response'))) + ->addMethodCall('addSubscriber', array(new Reference('listener.exception'))) + ; + $sc->register('framework', 'Simplex\Framework') + ->setArguments(array(new Reference('dispatcher'), new Reference('resolver'))) + ; + + return $sc; + +The goal of this file is to configure your objects and their dependencies. +Nothing is instantiated during this configuration step. This is purely a +static description of the objects you need to manipulate and how to create +them. Objects will be created on-demand when you access them from the +container or when the container needs them to create other objects. + +For instance, to create the router listener, we tell Symfony that its class +name is ``Symfony\Component\HttpKernel\EventListener\RouterListeners``, and +that its constructor takes a matcher object (``new Reference('matcher')``). As +you can see, each object is referenced by a name, a string that uniquely +identifies each object. The name allows us to get an object and to reference +it in other object definitions. + +.. note:: + + By default, every time you get an object from the container, it returns + the exact same instance. That's because a container manages your "global" + objects. + +The front controller is now only about wiring everything together:: + + get('framework')->handle($request); + + $response->send(); + +.. note:: + + If you want a light alternative for your container, consider `Pimple`_, a + simple dependency injection container in about 60 lines of PHP code. + +Now, here is how you can register a custom listener in the front controller:: + + $sc->register('listener.string_response', 'Simplex\StringResponseListener'); + $sc->getDefinition('dispatcher') + ->addMethodCall('addSubscriber', array(new Reference('listener.string_response'))) + ; + +Beside describing your objects, the dependency injection container can also be +configured via parameters. Let's create one that defines if we are in debug +mode or not:: + + $sc->setParameter('debug', true); + + echo $sc->getParameter('debug'); + +These parameters can be used when defining object definitions. Let's make the +charset configurable:: + + $sc->register('listener.response', 'Symfony\Component\HttpKernel\EventListener\ResponseListener') + ->setArguments(array('%charset%')) + ; + +After this change, you must set the charset before using the response listener +object:: + + $sc->setParameter('charset', 'UTF-8'); + +Instead of relying on the convention that the routes are defined by the +``$routes`` variables, let's use a parameter again:: + + $sc->register('matcher', 'Symfony\Component\Routing\Matcher\UrlMatcher') + ->setArguments(array('%routes%', new Reference('context'))) + ; + +And the related change in the front controller:: + + $sc->setParameter('routes', include __DIR__.'/../src/app.php'); + +We have obviously barely scratched the surface of what you can do with the +container: from class names as parameters, to overriding existing object +definitions, from scope support to dumping a container to a plain PHP class, +and much more. The Symfony dependency injection container is really powerful +and is able to manage any kind of PHP classes. + +Don't yell at me if you don't want to have a dependency injection container in +your framework. If you don't like it, don't use it. It's your framework, not +mine. + +This is (already) the last part of my series on creating a framework on top of +the Symfony2 components. I'm aware that many topics have not been covered in +great details, but hopefully it gives you enough information to get started on +your own and to better understand how the Symfony2 framework works internally. + +If you want to learn more, I highly recommend you to read the source code of +the Silex micro-framework, and especially its `Application`_ class. + +Have fun! + +~~ FIN ~~ + +*P.S.:* If there is enough interest (leave a comment on this post), I might +write some more articles on specific topics (using a configuration file for +routing, using HttpKernel debugging tools, using the build-in client to +simulate a browser are some of the topics that come to my mind for instance). + +.. _`Pimple`: https://github.com/fabpot/Pimple +.. _`Application`: https://github.com/fabpot/Silex/blob/master/src/Silex/Application.php From 26bef8d4d799513e995718fba6dbf38fa7b94ca1 Mon Sep 17 00:00:00 2001 From: Amitay Horwitz Date: Wed, 25 Jan 2012 09:44:00 +0200 Subject: [PATCH 0042/2667] Fixed typos --- book/part12.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/part12.rst b/book/part12.rst index 10a9b34f35a..b3eadcce4a3 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -229,7 +229,7 @@ We have obviously barely scratched the surface of what you can do with the container: from class names as parameters, to overriding existing object definitions, from scope support to dumping a container to a plain PHP class, and much more. The Symfony dependency injection container is really powerful -and is able to manage any kind of PHP classes. +and is able to manage any kind of PHP class. Don't yell at me if you don't want to have a dependency injection container in your framework. If you don't like it, don't use it. It's your framework, not @@ -249,7 +249,7 @@ Have fun! *P.S.:* If there is enough interest (leave a comment on this post), I might write some more articles on specific topics (using a configuration file for -routing, using HttpKernel debugging tools, using the build-in client to +routing, using HttpKernel debugging tools, using the built-in client to simulate a browser are some of the topics that come to my mind for instance). .. _`Pimple`: https://github.com/fabpot/Pimple From a8a2da0639b1d59dd500fac0f99a4cb85736ca1e Mon Sep 17 00:00:00 2001 From: Amitay Horwitz Date: Wed, 25 Jan 2012 09:44:00 +0200 Subject: [PATCH 0043/2667] Fixed part 12 typos --- book/part12.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/book/part12.rst b/book/part12.rst index 10a9b34f35a..97db3371a2d 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -2,7 +2,7 @@ Create your own framework... on top of the Symfony2 Components (part 12) ======================================================================== In the last installment of this series, we have emptied the -``Simplex\\Framework`` class by extending the ``HttpKernel`` class from +``Simplex\Framework`` class by extending the ``HttpKernel`` class from Symfony. Seeing this empty class, you might be tempted to move some code from the front controller to it:: @@ -229,7 +229,7 @@ We have obviously barely scratched the surface of what you can do with the container: from class names as parameters, to overriding existing object definitions, from scope support to dumping a container to a plain PHP class, and much more. The Symfony dependency injection container is really powerful -and is able to manage any kind of PHP classes. +and is able to manage any kind of PHP class. Don't yell at me if you don't want to have a dependency injection container in your framework. If you don't like it, don't use it. It's your framework, not @@ -249,7 +249,7 @@ Have fun! *P.S.:* If there is enough interest (leave a comment on this post), I might write some more articles on specific topics (using a configuration file for -routing, using HttpKernel debugging tools, using the build-in client to +routing, using HttpKernel debugging tools, using the built-in client to simulate a browser are some of the topics that come to my mind for instance). .. _`Pimple`: https://github.com/fabpot/Pimple From db5937455d4c77b79923b03eea5debbcdb4fc22e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 25 Jan 2012 09:53:45 +0100 Subject: [PATCH 0044/2667] fixed typo --- book/part12.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part12.rst b/book/part12.rst index 10a9b34f35a..2ff56c15bea 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -151,7 +151,7 @@ them. Objects will be created on-demand when you access them from the container or when the container needs them to create other objects. For instance, to create the router listener, we tell Symfony that its class -name is ``Symfony\Component\HttpKernel\EventListener\RouterListeners``, and +name is ``Symfony\Component\HttpKernel\EventListener\RouterListener``, and that its constructor takes a matcher object (``new Reference('matcher')``). As you can see, each object is referenced by a name, a string that uniquely identifies each object. The name allows us to get an object and to reference From d907d46c3c672f01f7ce3d55fd8c5dde0540f140 Mon Sep 17 00:00:00 2001 From: Arnaud Kleinpeter Date: Wed, 25 Jan 2012 14:44:04 +0100 Subject: [PATCH 0045/2667] Fixed typos --- book/part12.rst | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/book/part12.rst b/book/part12.rst index 10a9b34f35a..ac921fc0c89 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -3,8 +3,8 @@ Create your own framework... on top of the Symfony2 Components (part 12) In the last installment of this series, we have emptied the ``Simplex\\Framework`` class by extending the ``HttpKernel`` class from -Symfony. Seeing this empty class, you might be tempted to move some code from -the front controller to it:: +the eponymous component. Seeing this empty class, you might be tempted to move +some code from the front controller to it:: handle($request)->send(); -Having a more concise front controller means that you can have more than one +Having a concise front controller allows you to have several front controllers for a single application. Why would it be useful? To allow having different configuration for the development environment and the production one for instance. In the development environment, you might want to have error @@ -78,16 +78,20 @@ issues: * Related to the previous point, we cannot test our framework easily anymore as it's impossible to mock internal objects; -* We cannot change the charset passed to ResponseListener anymore (a +* We cannot change the charset passed to ``ResponseListener`` anymore (a workaround could be to pass it as a constructor argument). The previous code did not exhibit the same issues because we used dependency injection; all dependencies of our objects were injected into their -constructors (for instance, the event dispatcher were injected into the +constructors (for instance, the event dispatchers were injected into the framework so that we had total control of its creation and configuration). -Does it means that we have to make a choice between flexibility, +Does it mean that we have to make a choice between flexibility, +<<<<<<< .merge_file_8XJJxl customization, ease of testing and not having to copy and paste the same code +======= +customization, ease of testing and not to copy and paste the same code +>>>>>>> .merge_file_kv38Yk into each application front controller? As you might expect, there is a solution. We can solve all these issues and some more by using the Symfony2 dependency injection container: @@ -249,7 +253,7 @@ Have fun! *P.S.:* If there is enough interest (leave a comment on this post), I might write some more articles on specific topics (using a configuration file for -routing, using HttpKernel debugging tools, using the build-in client to +routing, using HttpKernel debugging tools, using the built-in client to simulate a browser are some of the topics that come to my mind for instance). .. _`Pimple`: https://github.com/fabpot/Pimple From 1f43dbfc5e550ab82ef64d6a4723e46067bb15c9 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 25 Jan 2012 15:55:08 +0100 Subject: [PATCH 0046/2667] removed conflict merge --- book/part12.rst | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/book/part12.rst b/book/part12.rst index f8174c24a58..83ac3977f5d 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -86,15 +86,11 @@ injection; all dependencies of our objects were injected into their constructors (for instance, the event dispatchers were injected into the framework so that we had total control of its creation and configuration). -Does it mean that we have to make a choice between flexibility, -<<<<<<< .merge_file_8XJJxl -customization, ease of testing and not having to copy and paste the same code -======= -customization, ease of testing and not to copy and paste the same code ->>>>>>> .merge_file_kv38Yk -into each application front controller? As you might expect, there is a -solution. We can solve all these issues and some more by using the Symfony2 -dependency injection container: +Does it mean that we have to make a choice between flexibility, customization, +ease of testing and not to copy and paste the same code into each application +front controller? As you might expect, there is a solution. We can solve all +these issues and some more by using the Symfony2 dependency injection +container: .. code-block:: json From 006b1e2efbfb75de4529f8e573b6aa22234b529c Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 27 Jan 2012 20:37:33 +0100 Subject: [PATCH 0047/2667] fixed markup --- book/part04.rst | 2 +- book/part12.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/book/part04.rst b/book/part04.rst index 6a168aeac89..7437aade78c 100644 --- a/book/part04.rst +++ b/book/part04.rst @@ -57,7 +57,7 @@ To support this feature, we are going to use the Symfony2 Routing component. As always, add it to ``composer.json`` and run the ``php composer.phar update`` command to install it: -.. code-block:: json +.. code-block:: javascript { "require": { diff --git a/book/part12.rst b/book/part12.rst index 83ac3977f5d..08978478bc3 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -92,7 +92,7 @@ front controller? As you might expect, there is a solution. We can solve all these issues and some more by using the Symfony2 dependency injection container: -.. code-block:: json +.. code-block:: javascript { "require": { From de69a874c1ecfd229c7a886918e918f39e7eef36 Mon Sep 17 00:00:00 2001 From: Stefan hr Berder Date: Tue, 7 Feb 2012 04:48:25 +0100 Subject: [PATCH 0048/2667] HttpKernel name can't be imported twice, if importing only Symfony\Component\HttpKernel\HttpKernel there will be problems later with HttpKernel subclasses (HttpKernel\Controller\ControllerResolver first and the others following). Could use 'use ... as ...' but I don't like it. --- book/part12.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/book/part12.rst b/book/part12.rst index 83ac3977f5d..deea9d642b7 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -12,12 +12,11 @@ some code from the front controller to it:: namespace Simplex; - use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\Routing; use Symfony\Component\HttpKernel; use Symfony\Component\EventDispatcher\EventDispatcher; - class Framework extends HttpKernel + class Framework extends HttpKernel\HttpKernel { public function __construct($routes) { From 400c087fee27ad39156b297fc0766f4c44d83eba Mon Sep 17 00:00:00 2001 From: Stefan hr Berder Date: Tue, 7 Feb 2012 05:09:57 +0100 Subject: [PATCH 0049/2667] add framework code as people would probably modify it following first code example (putting object creations in src/Simplex/Framework.php) --- book/part12.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/book/part12.rst b/book/part12.rst index deea9d642b7..8ef48ef0ee7 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -181,6 +181,20 @@ The front controller is now only about wiring everything together:: $response->send(); +As all the objects are now created in the dependency injection container, the framework code should be the previous simple version:: + + Date: Tue, 7 Feb 2012 05:56:28 +0100 Subject: [PATCH 0050/2667] updated titles --- book/part01.rst | 4 ++-- book/part02.rst | 4 ++-- book/part03.rst | 4 ++-- book/part04.rst | 4 ++-- book/part05.rst | 4 ++-- book/part06.rst | 4 ++-- book/part07.rst | 4 ++-- book/part08.rst | 4 ++-- book/part09.rst | 4 ++-- book/part10.rst | 4 ++-- book/part11.rst | 4 ++-- book/part12.rst | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/book/part01.rst b/book/part01.rst index cebc1b98503..092db5c4b51 100644 --- a/book/part01.rst +++ b/book/part01.rst @@ -1,5 +1,5 @@ -Create your own framework... on top of the Symfony2 Components (part 1) -======================================================================= +Introduction +============ Symfony2 is a reusable set of standalone, decoupled, and cohesive PHP components that solve common web development problems. diff --git a/book/part02.rst b/book/part02.rst index f2bc1502025..d76ee84d7c8 100644 --- a/book/part02.rst +++ b/book/part02.rst @@ -1,5 +1,5 @@ -Create your own framework... on top of the Symfony2 Components (part 2) -======================================================================= +The HttpFoundation Component +============================ Before we dive into the code refactoring, I first want to step back and take a look at why you would like to use a framework instead of keeping your diff --git a/book/part03.rst b/book/part03.rst index be9e5124fee..abf548815d0 100644 --- a/book/part03.rst +++ b/book/part03.rst @@ -1,5 +1,5 @@ -Create your own framework... on top of the Symfony2 Components (part 3) -======================================================================= +The Front Controller +==================== Up until now, our application is simplistic as there is only one page. To spice things up a little bit, let's go crazy and add another page that says diff --git a/book/part04.rst b/book/part04.rst index 7437aade78c..4222366b4b5 100644 --- a/book/part04.rst +++ b/book/part04.rst @@ -1,5 +1,5 @@ -Create your own framework... on top of the Symfony2 Components (part 4) -======================================================================= +The Routing Component +===================== Before we start with today's topic, let's refactor our current framework just a little to make templates even more readable:: diff --git a/book/part05.rst b/book/part05.rst index f657ceb5901..03b5d854716 100644 --- a/book/part05.rst +++ b/book/part05.rst @@ -1,5 +1,5 @@ -Create your own framework... on top of the Symfony2 Components (part 5) -======================================================================= +Templating +========== The astute reader has noticed that our framework hardcodes the way specific "code" (the templates) is run. For simple pages like the ones we have created diff --git a/book/part06.rst b/book/part06.rst index d70c31ddc20..aaa40de1057 100644 --- a/book/part06.rst +++ b/book/part06.rst @@ -1,5 +1,5 @@ -Create your own framework... on top of the Symfony2 Components (part 6) -======================================================================= +The HttpKernel Component: the Controller Resolver +================================================= You might think that our framework is already pretty solid and you are probably right. But let's see how we can improve it nonetheless. diff --git a/book/part07.rst b/book/part07.rst index 964c897b403..e6e0d76e56a 100644 --- a/book/part07.rst +++ b/book/part07.rst @@ -1,5 +1,5 @@ -Create your own framework... on top of the Symfony2 Components (part 7) -======================================================================= +The Separation of Concerns +========================== One down-side of our framework right now is that we need to copy and paste the code in ``front.php`` each time we create a new website. 40 lines of code is diff --git a/book/part08.rst b/book/part08.rst index 6e50b557ca7..35c00de6cf0 100644 --- a/book/part08.rst +++ b/book/part08.rst @@ -1,5 +1,5 @@ -Create your own framework... on top of the Symfony2 Components (part 8) -======================================================================= +Unit Testing +============ Some watchful readers pointed out some subtle but nonetheless important bugs in the framework we have built yesterday. When creating a framework, you must diff --git a/book/part09.rst b/book/part09.rst index c35b871565a..f06d1011d8b 100644 --- a/book/part09.rst +++ b/book/part09.rst @@ -1,5 +1,5 @@ -Create your own framework... on top of the Symfony2 Components (part 9) -======================================================================= +The EventDispatcher Component +============================= Our framework is still missing a major characteristic of any good framework: *extensibility*. Being extensible means that the developer should be able to diff --git a/book/part10.rst b/book/part10.rst index 7e32d77970a..b302ada39fe 100644 --- a/book/part10.rst +++ b/book/part10.rst @@ -1,5 +1,5 @@ -Create your own framework... on top of the Symfony2 Components (part 10) -======================================================================== +The HttpKernel Component: HttpKernelInterface +============================================= In the conclusion of the second part of this series, I've talked about one great benefit of using the Symfony2 components: the *interoperability* between diff --git a/book/part11.rst b/book/part11.rst index 1d55c653aac..c165a04c1bf 100644 --- a/book/part11.rst +++ b/book/part11.rst @@ -1,5 +1,5 @@ -Create your own framework... on top of the Symfony2 Components (part 11) -======================================================================== +The HttpKernel Component: The HttpKernel Class +============================================== If you were to use our framework right now, you would probably have to add support for custom error messages. Right now, we have 404 and 500 error diff --git a/book/part12.rst b/book/part12.rst index 08978478bc3..d1a6fa2ca6d 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -1,5 +1,5 @@ -Create your own framework... on top of the Symfony2 Components (part 12) -======================================================================== +The DependencyInjection Component +================================= In the last installment of this series, we have emptied the ``Simplex\\Framework`` class by extending the ``HttpKernel`` class from From aae0705d9cb986f1529a8834106a6b2e6ff4897f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 7 Feb 2012 05:57:11 +0100 Subject: [PATCH 0051/2667] fixed CS --- book/part12.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/book/part12.rst b/book/part12.rst index 5442570909f..0a2b7642cd5 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -181,7 +181,8 @@ The front controller is now only about wiring everything together:: $response->send(); -As all the objects are now created in the dependency injection container, the framework code should be the previous simple version:: +As all the objects are now created in the dependency injection container, the +framework code should be the previous simple version:: Date: Sat, 25 Feb 2012 14:48:31 +0100 Subject: [PATCH 0052/2667] Fixed one typo. --- book/part11.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part11.rst b/book/part11.rst index c165a04c1bf..ab17a38049c 100644 --- a/book/part11.rst +++ b/book/part11.rst @@ -75,7 +75,7 @@ make your error management configurable:: $msg = 'Something went wrong! ('.$exception->getMessage().')'; return new Response($msg, $exception->getStatusCode()); - }); + }; $dispatcher->addSubscriber(new HttpKernel\EventListener\ExceptionListener($errorHandler)); ``ExceptionListener`` gives you a ``FlattenException`` instance instead of the From 8e7106d7309c57d5c112158ed32986018748802c Mon Sep 17 00:00:00 2001 From: ubick Date: Mon, 30 Apr 2012 13:53:48 +0200 Subject: [PATCH 0053/2667] Fixed a typo in part02.rst ( Date: Fri, 14 Sep 2012 09:15:23 +0300 Subject: [PATCH 0054/2667] Fix little typo --- book/part09.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part09.rst b/book/part09.rst index f06d1011d8b..28057eaad06 100644 --- a/book/part09.rst +++ b/book/part09.rst @@ -275,7 +275,7 @@ there is a solution: use subscribers instead of listeners:: $dispatcher->addSubscriber(new Simplex\ContentLengthListener()); $dispatcher->addSubscriber(new Simplex\GoogleListener()); -A subscriber knowns about all the events it is interested in and pass this +A subscriber knows about all the events it is interested in and pass this information to the dispatcher via the ``getSubscribedEvents()`` method. Hav 8000 e a look at the new version of the ``GoogleListener``:: From 91e46f6e7307eedc0ab337209693a0b853ba5dc7 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 29 Sep 2012 00:29:01 +0200 Subject: [PATCH 0055/2667] removed the paragraph about CS as we now have standards --- book/part01.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/book/part01.rst b/book/part01.rst index 092db5c4b51..b4f2bf65402 100644 --- a/book/part01.rst +++ b/book/part01.rst @@ -93,14 +93,6 @@ To store our framework, create a directory somewhere on your machine: $ mkdir framework $ cd framework -Coding Standards -~~~~~~~~~~~~~~~~ - -Before anyone starts a flame war about coding standards and why the one used -here suck hard, let's all admit that this does not matter that much as long as -you are consistent. For this book, we are going to use the `Symfony2 Coding -Standards`_. - Components Installation ~~~~~~~~~~~~~~~~~~~~~~~ From 799e963a7e7a00afcdbefaa500a3b418bfd5ae5a Mon Sep 17 00:00:00 2001 From: Bilal Amarni Date: Wed, 3 Oct 2012 12:58:58 +0200 Subject: [PATCH 0056/2667] updated composer autoload path --- book/part04.rst | 4 ++-- book/part05.rst | 2 +- book/part06.rst | 2 +- book/part09.rst | 2 +- book/part11.rst | 2 +- book/part12.rst | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/book/part04.rst b/book/part04.rst index 4222366b4b5..4983d5a9072 100644 --- a/book/part04.rst +++ b/book/part04.rst @@ -75,7 +75,7 @@ reference in ``front.php``:: // example.com/web/front.php - require_once __DIR__.'/../vendor/.composer/autoload.php'; + require_once __DIR__.'/../vendor/autoload.php'; // ... @@ -155,7 +155,7 @@ With this knowledge in mind, let's write the new version of our framework:: // example.com/web/front.php - require_once __DIR__.'/../vendor/.composer/autoload.php'; + require_once __DIR__.'/../vendor/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; diff --git a/book/part05.rst b/book/part05.rst index 03b5d854716..a10d9a7a21a 100644 --- a/book/part05.rst +++ b/book/part05.rst @@ -106,7 +106,7 @@ Here is the updated and improved version of our framework:: // example.com/web/front.php - require_once __DIR__.'/../vendor/.composer/autoload.php'; + require_once __DIR__.'/../vendor/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; diff --git a/book/part06.rst b/book/part06.rst index aaa40de1057..abce4d8e88a 100644 --- a/book/part06.rst +++ b/book/part06.rst @@ -158,7 +158,7 @@ Let's conclude with the new version of our framework:: // example.com/web/front.php - require_once __DIR__.'/../vendor/.composer/autoload.php'; + require_once __DIR__.'/../vendor/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; diff --git a/book/part09.rst b/book/part09.rst index 28057eaad06..efca2b9d421 100644 --- a/book/part09.rst +++ b/book/part09.rst @@ -136,7 +136,7 @@ the registration of a listener for the ``response`` event:: // example.com/web/front.php - require_once __DIR__.'/../vendor/.composer/autoload.php'; + require_once __DIR__.'/../vendor/autoload.php'; // ... diff --git a/book/part11.rst b/book/part11.rst index ab17a38049c..79ea6bf3849 100644 --- a/book/part11.rst +++ b/book/part11.rst @@ -40,7 +40,7 @@ And the new front controller:: // example.com/web/front.php - require_once __DIR__.'/../vendor/.composer/autoload.php'; + require_once __DIR__.'/../vendor/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; diff --git a/book/part12.rst b/book/part12.rst index 482cd0c0fd8..8f08531116e 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -38,7 +38,7 @@ The front controller code would become more concise:: // example.com/web/front.php - require_once __DIR__.'/../vendor/.composer/autoload.php'; + require_once __DIR__.'/../vendor/autoload.php'; use Symfony\Component\HttpFoundation\Request; @@ -168,7 +168,7 @@ The front controller is now only about wiring everything together:: // example.com/web/front.php - require_once __DIR__.'/../vendor/.composer/autoload.php'; + require_once __DIR__.'/../vendor/autoload.php'; use Symfony\Component\HttpFoundation\Request; From c88c20a20473214b890bd17416fdc922877ee975 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 15 Oct 2012 14:50:50 +0200 Subject: [PATCH 0057/2667] removed usage of the ClassLoader component in favor of Composer --- book/part01.rst | 64 ++++++++++--------------------------------------- book/part02.rst | 27 +++++++-------------- book/part03.rst | 9 ++++--- book/part04.rst | 15 +----------- book/part06.rst | 1 - book/part07.rst | 1 - book/part08.rst | 2 +- book/part09.rst | 1 - book/part12.rst | 1 - 9 files changed, 26 insertions(+), 95 deletions(-) diff --git a/book/part01.rst b/book/part01.rst index b4f2bf65402..c6342833315 100644 --- a/book/part01.rst +++ b/book/part01.rst @@ -64,9 +64,9 @@ Propel, or plain-old PDO for the Model; PHP or Twig for the View). When creating a framework, following the MVC pattern is not the right goal. The main goal should be the Separation of Concerns; I actually think that this is the only design pattern that you should really care about. The fundamental -principles of the Symfony2 Components are centered around the HTTP -specification. As such, the frameworks that we are going to create should be -more accurately labelled as HTTP frameworks or Request/Response frameworks. +principles of the Symfony2 Components are focused on the HTTP specification. +As such, the frameworks that we are going to create should be more accurately +labelled as HTTP frameworks or Request/Response frameworks. Before we start --------------- @@ -97,20 +97,18 @@ Components Installation ~~~~~~~~~~~~~~~~~~~~~~~ To install the Symfony2 Components that we need for our framework, we are -going to use `Composer`_, a project dependency manager for PHP. First, list -your dependencies in a ``composer.json`` file: +going to use `Composer`_, a project dependency manager for PHP. Create a +``composer.json`` file, where we will list our dependencies: .. code-block:: javascript { "require": { - "symfony/class-loader": "2.1.*" } } -Here, we tell Composer that our project depends on the Symfony2 ClassLoader -component, version 2.1.0 or later. To actually install the project -dependencies, download the composer binary and run it: +The file is empty for now as we do not depend on anything yet. To install the +project dependencies, download the composer binary and run it: .. code-block:: sh @@ -121,13 +119,7 @@ dependencies, download the composer binary and run it: $ php composer.phar install After running the ``install`` command, you must see a new ``vendor/`` -directory that must contain the Symfony2 ClassLoader code. - -.. note:: - - Even if we highly recommend you the use of Composer, you can also download - the archives of the components directly or use Git submodules. That's - really up to you. +directory. Naming Conventions and Autoloading ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -137,42 +129,8 @@ require the file where a class is defined before being able to use it. But with some conventions, we can just let PHP do the hard work for us. Symfony2 follows the de-facto PHP standard, `PSR-0`_, for class names and -autoloading. The Symfony2 ClassLoader Component provides an autoloader that -implements this PSR-0 standard and most of the time, the Symfony2 ClassLoader -is all you need to autoload all your project classes. - -Create an empty autoloader in a new ``autoload.php`` file: - -.. code-block:: php - - register(); - -You can now run the ``autoload.php`` on the CLI, it should not do anything and -should not throw any error: - -.. code-block:: sh - - $ php autoload.php - -.. tip:: - - The Symfony website has more information about the `ClassLoader`_ - component. - -.. note:: - - Composer automatically creates an autoloader for all your installed - dependencies; instead of using the ClassLoader component, you can also - just require ``vendor/.composer/autoload.php``. +autoloading and Composer generates such an autoloader for all the dependencies +it manages; it can be enabled by requiring the ``vendor/autoload.php`` file. Our Project ----------- @@ -183,6 +141,8 @@ start with the simplest web application we can think of in PHP:: registerNamespace('Symfony\\Component\\HttpFoundation', __DIR__.'/vendor/symfony/http-foundation'); - Now, let's rewrite our application by using the ``Request`` and the ``Response`` classes:: @@ -158,7 +147,7 @@ Now, let's rewrite our application by using the ``Request`` and the // framework/index.php - require_once __DIR__.'/autoload.php'; + require_once __DIR__.'/vendor/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; diff --git a/book/part03.rst b/book/part03.rst index abf548815d0..d795d57b565 100644 --- a/book/part03.rst +++ b/book/part03.rst @@ -9,7 +9,7 @@ goodbye:: // framework/bye.php - require_once __DIR__.'/autoload.php'; + require_once __DIR__.'/vendor/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -31,7 +31,7 @@ include file:: // framework/init.php - require_once __DIR__.'/autoload.php'; + require_once __DIR__.'/vendor/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -87,7 +87,7 @@ Such a script might look like the following:: // framework/front.php - require_once __DIR__.'/autoload.php'; + require_once __DIR__.'/vendor/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -160,7 +160,6 @@ outside the web root directory: example.com ├── composer.json │ src - │ ├── autoload.php │ └── pages │ ├── hello.php │ └── bye.php @@ -212,7 +211,7 @@ We have our framework for today:: // example.com/web/front.php - require_once __DIR__.'/../src/autoload.php'; + require_once __DIR__.'/../vendor/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; diff --git a/book/part04.rst b/book/part04.rst index 4983d5a9072..668e98f36c9 100644 --- a/book/part04.rst +++ b/book/part04.rst @@ -8,7 +8,7 @@ a little to make templates even more readable:: // example.com/web/front.php - require_once __DIR__.'/../src/autoload.php'; + require_once __DIR__.'/../vendor/autoload.php'; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -61,24 +61,11 @@ update`` command to install it: { "require": { - "symfony/class-loader": "2.1.*", "symfony/http-foundation": "2.1.*", "symfony/routing": "2.1.*" } } -From now on, we are going to use the generated Composer autoloader instead of -our own ``autoload.php``. Remove the ``autoload.php`` file and replace its -reference in ``front.php``:: - - diff --git a/book/part09.rst b/book/part09.rst index efca2b9d421..3ba1257ed41 100644 --- a/book/part09.rst +++ b/book/part09.rst @@ -21,7 +21,6 @@ version of this pattern: { "require": { - "symfony/class-loader": "2.1.*", "symfony/http-foundation": "2.1.*", "symfony/routing": "2.1.*", "symfony/http-kernel": "2.1.*", diff --git a/book/part12.rst b/book/part12.rst index 8f08531116e..4cd4c36dbf7 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -95,7 +95,6 @@ container: { "require": { - "symfony/class-loader": "2.1.*", "symfony/http-foundation": "2.1.*", "symfony/routing": "2.1.*", "symfony/http-kernel": "2.1.*", From 10e27324571a86ec256f7f13829b864cca306293 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 15 May 2013 19:09:49 +0200 Subject: [PATCH 0058/2667] added an index file --- book/index.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 book/index.rst diff --git a/book/index.rst b/book/index.rst new file mode 100644 index 00000000000..120c334d0f1 --- /dev/null +++ b/book/index.rst @@ -0,0 +1,17 @@ +Create your PHP Framework +========================= + +.. toctree:: + + part01 + part02 + part03 + part04 + part05 + part06 + part07 + part08 + part09 + part10 + part11 + part12 From f6656e49c8215d46d25afafea6da816d65344fe9 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 15 May 2013 19:16:44 +0200 Subject: [PATCH 0059/2667] removed a note that is not relevant anymore --- book/part12.rst | 7 ------- 1 file changed, 7 deletions(-) diff --git a/book/part12.rst b/book/part12.rst index 4cd4c36dbf7..0a8e6dc1762 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -258,12 +258,5 @@ the Silex micro-framework, and especially its `Application`_ class. Have fun! -~~ FIN ~~ - -*P.S.:* If there is enough interest (leave a comment on this post), I might -write some more articles on specific topics (using a configuration file for -routing, using HttpKernel debugging tools, using the built-in client to -simulate a browser are some of the topics that come to my mind for instance). - .. _`Pimple`: https://github.com/fabpot/Pimple .. _`Application`: https://github.com/fabpot/Silex/blob/master/src/Silex/Application.php From 249b7041d3eafe0b89f48a14dd76092fb3072df6 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 15 May 2013 19:17:26 +0200 Subject: [PATCH 0060/2667] added missing links --- book/part01.rst | 3 ++- book/part12.rst | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/book/part01.rst b/book/part01.rst index c6342833315..e27a9de3b12 100644 --- a/book/part01.rst +++ b/book/part01.rst @@ -1,7 +1,7 @@ Introduction ============ -Symfony2 is a reusable set of standalone, decoupled, and cohesive PHP +`Symfony2`_ is a reusable set of standalone, decoupled, and cohesive PHP components that solve common web development problems. Instead of using these low-level components, you can use the ready-to-be-used @@ -150,6 +150,7 @@ start with the simplest web application we can think of in PHP:: That's all for the first part of this series. Next time, we will introduce the HttpFoundation Component and see what it brings us. +.. _`Symfony2`: http://symfony.com/ .. _`documentation`: http://symfony.com/doc .. _`Silex`: http://silex.sensiolabs.org/ .. _`autoload`: http://fr.php.net/autoload diff --git a/book/part12.rst b/book/part12.rst index 0a8e6dc1762..19b34a546a3 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -254,9 +254,10 @@ great details, but hopefully it gives you enough information to get started on your own and to better understand how the Symfony2 framework works internally. If you want to learn more, I highly recommend you to read the source code of -the Silex micro-framework, and especially its `Application`_ class. +the `Silex`_ micro-framework, and especially its `Application`_ class. Have fun! .. _`Pimple`: https://github.com/fabpot/Pimple +.. _`Silex`: https://silex.sensiolabs.org/ .. _`Application`: https://github.com/fabpot/Silex/blob/master/src/Silex/Application.php From ca9d5d8e26315b7091f49ac24059cbe7e8702582 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 15 May 2013 19:18:35 +0200 Subject: [PATCH 0061/2667] removed unused references --- book/part01.rst | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/book/part01.rst b/book/part01.rst index e27a9de3b12..b65e4f257a1 100644 --- a/book/part01.rst +++ b/book/part01.rst @@ -150,11 +150,9 @@ start with the simplest web application we can think of in PHP:: That's all for the first part of this series. Next time, we will introduce the HttpFoundation Component and see what it brings us. -.. _`Symfony2`: http://symfony.com/ -.. _`documentation`: http://symfony.com/doc -.. _`Silex`: http://silex.sensiolabs.org/ -.. _`autoload`: http://fr.php.net/autoload -.. _`Composer`: http://packagist.org/about-composer -.. _`PSR-0`: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md -.. _`Symfony2 Coding Standards`: http://symfony.com/doc/current/contributing/code/standards.html -.. _`ClassLoader`: http://symfony.com/doc/current/components/class_loader.html +.. _`Symfony2`: http://symfony.com/ +.. _`documentation`: http://symfony.com/doc +.. _`Silex`: http://silex.sensiolabs.org/ +.. _`autoload`: http://fr.php.net/autoload +.. _`Composer`: http://packagist.org/about-composer +.. _`PSR-0`: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md From f3c151cff8b0bd29c3522b41d6230ac9ff298d63 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 15 May 2013 19:27:42 +0200 Subject: [PATCH 0062/2667] reworded slightly some sentence to convert the text from a series of articles to a book --- book/part01.rst | 12 ++++++------ book/part02.rst | 9 ++++----- book/part04.rst | 4 ++-- book/part08.rst | 14 +++++++------- book/part09.rst | 2 +- book/part10.rst | 2 +- book/part11.rst | 12 ++++++------ book/part12.rst | 17 +++++++++-------- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/book/part01.rst b/book/part01.rst index b65e4f257a1..513cb82d243 100644 --- a/book/part01.rst +++ b/book/part01.rst @@ -6,7 +6,7 @@ components that solve common web development problems. Instead of using these low-level components, you can use the ready-to-be-used Symfony2 full-stack web framework, which is based on these components... or -you can create your very own framework. This series is about the latter. +you can create your very own framework. This book is about the latter. .. note:: @@ -48,7 +48,7 @@ Symfony2 Components. .. tip:: - If you don't have time to read the whole series, or if you want to get + If you don't have time to read the whole book, or if you want to get started fast, you can also have a look at `Silex`_, a micro-framework based on the Symfony2 Components. The code is rather slim and it leverages many aspects of the Symfony2 Components. @@ -56,8 +56,8 @@ Symfony2 Components. Many modern web frameworks call themselves MVC frameworks. We won't talk about MVC here as the Symfony2 Components are able to create any type of frameworks, not just the ones that follow the MVC architecture. Anyway, if you have a look -at the MVC semantics, this series is about how to create the Controller part -of a framework. For the Model and the View, it really depends on your personal +at the MVC semantics, this book is about how to create the Controller part of +a framework. For the Model and the View, it really depends on your personal taste and I will let you use any existing third-party libraries (Doctrine, Propel, or plain-old PDO for the Model; PHP or Twig for the View). @@ -147,8 +147,8 @@ start with the simplest web application we can think of in PHP:: printf('Hello %s', $input); -That's all for the first part of this series. Next time, we will introduce the -HttpFoundation Component and see what it brings us. +In the next chapter, we are going to introduce the HttpFoundation Component +and see what it brings us. .. _`Symfony2`: http://symfony.com/ .. _`documentation`: http://symfony.com/doc diff --git a/book/part02.rst b/book/part02.rst index b000de0d201..c86da9deabb 100644 --- a/book/part02.rst +++ b/book/part02.rst @@ -15,8 +15,8 @@ framework from scratch. developers; the Internet has already plenty of good resources on that topic. -Even if the "application" we wrote yesterday was simple enough, it suffers -from a few problems:: +Even if the "application" we wrote in the previous chapter was simple enough, +it suffers from a few problems:: Date: Wed, 15 May 2013 20:06:43 +0200 Subject: [PATCH 0063/2667] updated code for Symfony 2.3 and made minor tweaks to the text --- book/part01.rst | 25 +++++++++++++------------ book/part02.rst | 17 +++++++++-------- book/part03.rst | 12 ++++++------ book/part04.rst | 13 +++---------- book/part05.rst | 12 ++++++------ book/part06.rst | 8 ++++---- book/part07.rst | 8 ++++---- book/part09.rst | 10 +++++----- book/part11.rst | 2 +- book/part12.rst | 12 ++++++------ 10 files changed, 57 insertions(+), 62 deletio 8000 ns(-) diff --git a/book/part01.rst b/book/part01.rst index 513cb82d243..b22945c1365 100644 --- a/book/part01.rst +++ b/book/part01.rst @@ -53,20 +53,21 @@ Symfony2 Components. based on the Symfony2 Components. The code is rather slim and it leverages many aspects of the Symfony2 Components. -Many modern web frameworks call themselves MVC frameworks. We won't talk about -MVC here as the Symfony2 Components are able to create any type of frameworks, -not just the ones that follow the MVC architecture. Anyway, if you have a look -at the MVC semantics, this book is about how to create the Controller part of -a framework. For the Model and the View, it really depends on your personal -taste and I will let you use any existing third-party libraries (Doctrine, -Propel, or plain-old PDO for the Model; PHP or Twig for the View). +Many modern web frameworks advertize themselves as being MVC frameworks. We +won't talk about the MVC pattern as the Symfony2 Components are able to create +any type of frameworks, not just the ones that follow the MVC architecture. +Anyway, if you have a look at the MVC semantics, this book is about how to +create the Controller part of a framework. For the Model and the View, it +really depends on your personal taste and I will let you use any existing +third-party libraries (Doctrine, Propel, or plain-old PDO for the Model; PHP +or Twig for the View). When creating a framework, following the MVC pattern is not the right goal. -The main goal should be the Separation of Concerns; I actually think that this -is the only design pattern that you should really care about. The fundamental -principles of the Symfony2 Components are focused on the HTTP specification. -As such, the frameworks that we are going to create should be more accurately -labelled as HTTP frameworks or Request/Response frameworks. +The main goal should be the **Separation of Concerns**; I actually think that +this is the only design pattern that you should really care about. The +fundamental principles of the Symfony2 Components are focused on the HTTP +specification. As such, the frameworks that we are going to create should be +more accurately labelled as HTTP frameworks or Request/Response frameworks. Before we start --------------- diff --git a/book/part02.rst b/book/part02.rst index c86da9deabb..226547a6ab1 100644 --- a/book/part02.rst +++ b/book/part02.rst @@ -129,7 +129,7 @@ dependency for the project: { "require": { - "symfony/http-foundation": "2.1.*" + "symfony/http-foundation": "~2.3" } } @@ -170,8 +170,8 @@ first outputs the HTTP headers followed by the content). Before the ``send()`` call, we should have added a call to the ``prepare()`` method (``$response->prepare($request);``) to ensure that our Response were compliant with the HTTP specification. For instance, if - we were to call the page with the ``HEAD`` method, it would have removed - the content of the Response. + we were to call the page with the ``HEAD`` method, it would remove the + content of the Response. The main difference with the previous code is that you have total control of the HTTP messages. You can create whatever request you want and you are in @@ -275,11 +275,12 @@ secure? The ``$_SERVER['HTTP_X_FORWARDED_FOR']`` value cannot be trusted as it can be manipulated by the end user when there is no proxy. So, if you are using this code in production without a proxy, it becomes trivially easy to abuse your system. That's not the case with the ``getClientIp()`` method as -you must explicitly trust this header by calling ``trustProxyData()``:: +you must explicitly trust your reverse proxies by calling +``setTrustedProxies()``:: getClientIp(true)) { // the client is a known one, so give it some more privilege @@ -302,9 +303,9 @@ Using just the Symfony2 HttpFoundation component already allows you to write better and more testable code. It also allows you to write code faster as many day-to-day problems have already been solved for you. -As a matter of fact, projects like Drupal have adopted (for the upcoming -version 8) the HttpFoundation component; if it works for them, it will -probably work for you. Don't reinvent the wheel. +As a matter of fact, projects like Drupal have adopted the HttpFoundation +component; if it works for them, it will probably work for you. Don't reinvent +the wheel. I've almost forgot to talk about one added benefit: using the HttpFoundation component is the start of better interoperability between all frameworks and diff --git a/book/part03.rst b/book/part03.rst index d795d57b565..4ca7ee1cc51 100644 --- a/book/part03.rst +++ b/book/part03.rst @@ -65,8 +65,8 @@ And for the "Goodbye" page:: We have indeed moved most of the shared code into a central place, but it does not feel like a good abstraction, doesn't it? First, we still have the -``send()`` method in all pages, then our pages does not look like templates, -and we are still not able to test this code properly. +``send()`` method in all pages, then our pages do not look like templates, and +we are still not able to test this code properly. Moreover, adding a new page means that we need to create a new PHP script, which name is exposed to the end user via the URL @@ -140,9 +140,9 @@ To access a page, you must now use the ``front.php`` script: able to type ``http://example.com/hello?name=Fabien``, which looks much better. -So, the trick is the usage of the ``Request::getPathInfo()`` method which -returns the path of the Request by removing the front controller script name -including its sub-directories (only if needed -- see above tip). +The trick is the usage of the ``Request::getPathInfo()`` method which returns +the path of the Request by removing the front controller script name including +its sub-directories (only if needed -- see above tip). .. tip:: @@ -205,7 +205,7 @@ And the ``hello.php`` script can now be converted to a template:: Hello -We have our framework for today:: +We have the first version of our framework:: -* Routes configuration has been moved to its own file: +* Route configuration has been moved to its own file: .. code-block:: php @@ -232,11 +232,4 @@ generate absolute URLs:: echo $dumper->dump(); - Want even more performance? Dump your routes as a set of Apache rewrite - rules:: - - $dumper = new Routing\Matcher\Dumper\ApacheMatcherDumper($routes); - - echo $dumper->dump(); - .. _`documentation`: http://symfony.com/doc/current/components/routing.html diff --git a/book/part05.rst b/book/part05.rst index a10d9a7a21a..f2494c332e7 100644 --- a/book/part05.rst +++ b/book/part05.rst @@ -33,8 +33,8 @@ As the rendering is now done by an external function (``render_template()`` here), we need to pass to it the attributes extracted from the URL. We could have passed them as an additional argument to ``render_template()``, but instead, let's use another feature of the ``Request`` class called -*attributes*: Request attributes lets you attach additional information about -the Request that is not directly related to the HTTP Request data. +*attributes*: Request attributes is a way to attach additional information +about the Request that is not directly related to the HTTP Request data. You can now create the ``render_template()`` function, a generic controller that renders a template when there is no specific logic. To keep the same @@ -177,10 +177,10 @@ framework does not need to be modified in any way, just create a new return $routes; The ``is_leap_year()`` function returns ``true`` when the given year is a leap -year, ``false`` otherwise. If the year is null, the current year is tested. -The controller is simple: it gets the year from the request attributes, pass -it to the `is_leap_year()`` function, and according to the return value it -creates a new Response object. +year, ``false`` otherwise. If the year is ``null``, the current year is +tested. The controller is simple: it gets the year from the request +attributes, pass it to the `is_leap_year()`` function, and according to the +return value it creates a new Response object. As always, you can decide to stop here and use the framework as is; it's probably all you need to create simple websites like those fancy one-page diff --git a/book/part06.rst b/book/part06.rst index 868ea17a057..d7d721c73ef 100644 --- a/book/part06.rst +++ b/book/part06.rst @@ -41,9 +41,9 @@ component:: { "require": { - "symfony/http-foundation": "2.1.*", - "symfony/routing": "2.1.*", - "symfony/http-kernel": "2.1.*" + "symfony/http-foundation": "~2.3", + "symfony/routing": "~2.3", + "symfony/http-kernel": "~2.3" } } @@ -145,7 +145,7 @@ method is not defined, an argument has no matching attribute, ...). With the great flexibility of the default controller resolver, you might wonder why someone would want to create another one (why would there be an - interface if not). Two examples: in Symfony2, ``getController()`` is + interface if not?). Two examples: in Symfony2, ``getController()`` is enhanced to support `controllers as services`_; and in `FrameworkExtraBundle`_, ``getArguments()`` is enhanced to support parameter converters, where request attributes are converted to objects diff --git a/book/part07.rst b/book/part07.rst index 76f95a3df0e..004ca4a140d 100644 --- a/book/part07.rst +++ b/book/part07.rst @@ -89,12 +89,12 @@ be autoloaded, update the ``composer.json`` file: { "require": { - "symfony/http-foundation": "2.1.*", - "symfony/routing": "2.1.*", - "symfony/http-kernel": "2.1.*" + "symfony/http-foundation": "~2.3", + "symfony/routing": "~2.3", + "symfony/http-kernel": "~2.3" }, "autoload": { - "psr-0": { "Simplex": "src/", "Calendar": "src/" } + "psr-0": { "Simplex\\": "src/", "Calendar\\": "src/" } } } diff --git a/book/part09.rst b/book/part09.rst index 53649990457..05604afce89 100644 --- a/book/part09.rst +++ b/book/part09.rst @@ -21,13 +21,13 @@ version of this pattern: { "require": { - "symfony/http-foundation": "2.1.*", - "symfony/routing": "2.1.*", - "symfony/http-kernel": "2.1.*", - "symfony/event-dispatcher": "2.1.*" + "symfony/http-foundation": "~2.3", + "symfony/routing": "~2.3", + "symfony/http-kernel": "~2.3", + "symfony/event-dispatcher": "~2.3" }, "autoload": { - "psr-0": { "Simplex": "src/", "Calendar": "src/" } + "psr-0": { "Simplex\\": "src/", "Calendar\\": "src/" } } } diff --git a/book/part11.rst b/book/part11.rst index 1e29e214fc5..0352fbf4fba 100644 --- a/book/part11.rst +++ b/book/part11.rst @@ -110,7 +110,7 @@ The error controller reads as follows:: Voilà! Clean and customizable error management without efforts. And of course, if your controller throws an exception, HttpKernel will handle it nicely. -In part 2, we have talked about the ``Response::prepare()`` method, which +In chapter two, we talked about the ``Response::prepare()`` method, which ensures that a Response is compliant with the HTTP specification. It is probably a good idea to always call it just before sending the Response to the client; that's what the ``ResponseListener`` does:: diff --git a/book/part12.rst b/book/part12.rst index 0b79feae509..41330f5666c 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -95,14 +95,14 @@ container: { "require": { - "symfony/http-foundation": "2.1.*", - "symfony/routing": "2.1.*", - "symfony/http-kernel": "2.1.*", - "symfony/event-dispatcher": "2.1.*", - "symfony/dependency-injection": "2.1.*" + "symfony/http-foundation": "~2.3", + "symfony/routing": "~2.3", + "symfony/http-kernel": "~2.3", + "symfony/event-dispatcher": "~2.3", + "symfony/dependency-injection": "~2.3" }, "autoload": { - "psr-0": { "Simplex": "src/", "Calendar": "src/" } + "psr-0": { "Simplex\\": "src/", "Calendar\\": "src/" } } } From a1336e042316f591b365a59a1915ea606605865e Mon Sep 17 00:00:00 2001 From: revollat Date: Thu, 6 Jun 2013 16:51:52 +0300 Subject: [PATCH 0064/2667] Update part06.rst If $request is not typed there is an error : Controller "render_template" requires that you provide a value for the "$request" argument (because there is no default value or because there is a non optional argument after this one). --- book/part06.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/part06.rst b/book/part06.rst index d7d721c73ef..aa7b534e4d0 100644 --- a/book/part06.rst +++ b/book/part06.rst @@ -164,7 +164,7 @@ Let's conclude with the new version of our framework:: use Symfony\Component\Routing; use Symfony\Component\HttpKernel; - function render_template($request) + function render_template(Request $request) { extract($request->attributes->all()); ob_start(); From 55f5c12fd61c0e5863c561330ef35dc06bd08a2b Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 23 Jun 2014 09:24:02 +0200 Subject: [PATCH 0065/2667] updated the whole book (changes mainly related to Composer) --- book/part01.rst | 59 ++++++++++++++++++------------------------------- book/part02.rst | 31 ++++++++++++++------------ book/part03.rst | 34 ++++++++++++++++------------ book/part04.rst | 13 +++-------- book/part06.rst | 12 ++++------ book/part07.rst | 14 +++++------- book/part09.rst | 14 ++---------- book/part12.rst | 17 +++----------- 8 files changed, 77 insertions(+), 117 deletions(-) diff --git a/book/part01.rst b/book/part01.rst index b22945c1365..acdd97284f1 100644 --- a/book/part01.rst +++ b/book/part01.rst @@ -94,44 +94,23 @@ To store our framework, create a directory somewhere on your machine: $ mkdir framework $ cd framework -Components Installation -~~~~~~~~~~~~~~~~~~~~~~~ +Dependency Management +~~~~~~~~~~~~~~~~~~~~~ -To install the Symfony2 Components that we need for our framework, we are -going to use `Composer`_, a project dependency manager for PHP. Create a -``composer.json`` file, where we will list our dependencies: - -.. code-block:: javascript - - { - "require": { - } - } - -The file is empty for now as we do not depend on anything yet. To install the -project dependencies, download the composer binary and run it: +To install the Symfony2 Components that we need for our framework, we are going +to use `Composer`_, a project dependency manager for PHP. If you don't have it +yet, `download and install`_ Composer now: .. code-block:: sh - $ wget http://getcomposer.org/composer.phar - $ # or - $ curl -O http://getcomposer.org/composer.phar - - $ php composer.phar install - -After running the ``install`` command, you must see a new ``vendor/`` -directory. + $ curl -sS https://getcomposer.org/installer | php -Naming Conventions and Autoloading -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Then, generate an empty ``composer.json`` file, where Composer will store the +framework dependencies: -We are going to `autoload`_ all our classes. Without autoloading, you need to -require the file where a class is defined before being able to use it. But -with some conventions, we can just let PHP do the hard work for us. +.. code-block:: sh -Symfony2 follows the de-facto PHP standard, `PSR-0`_, for class names and -autoloading and Composer generates such an autoloader for all the dependencies -it manages; it can be enabled by requiring the ``vendor/autoload.php`` file. + $ php composer.phar init -n Our Project ----------- @@ -148,12 +127,18 @@ start with the simplest web application we can think of in PHP:: printf('Hello %s', $input); +Use the PHP built-in server to test this great application in a browser +(``http://localhost:4321/index.php?name=Fabien``): + +.. code-block:: sh + + $ php -S 127.0.0.1:4321 + In the next chapter, we are going to introduce the HttpFoundation Component and see what it brings us. -.. _`Symfony2`: http://symfony.com/ -.. _`documentation`: http://symfony.com/doc -.. _`Silex`: http://silex.sensiolabs.org/ -.. _`autoload`: http://fr.php.net/autoload -.. _`Composer`: http://packagist.org/about-composer -.. _`PSR-0`: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md +.. _`Symfony2`: http://symfony.com/ +.. _`documentation`: http://symfony.com/doc +.. _`Silex`: http://silex.sensiolabs.org/ +.. _`Composer`: http://packagist.org/about-composer +.. _`download and install`: https://getcomposer.org/doc/01-basic-usage.md diff --git a/book/part02.rst b/book/part02.rst index 226547a6ab1..f5c007990a7 100644 --- a/book/part02.rst +++ b/book/part02.rst @@ -26,7 +26,7 @@ it suffers from a few problems:: printf('Hello %s', $input); -First, if the ``name`` query parameter is not given in the URL query string, +First, if the ``name`` query parameter is not defined in the URL query string, you will get a PHP warning; so let's fix it:: send(); We have indeed moved most of the shared code into a central place, but it does -not feel like a good abstraction, doesn't it? First, we still have the -``send()`` method in all pages, then our pages do not look like templates, and -we are still not able to test this code properly. +not feel like a good abstraction, does it? We still have the ``send()`` method +for all pages, our pages do not look like templates, and we are still not able +to test this code properly. Moreover, adding a new page means that we need to create a new PHP script, which name is exposed to the end user via the URL -(``http://example.com/bye.php``): there is a direct mapping between the PHP +(``http://127.0.0.1:4321/bye.php``): there is a direct mapping between the PHP script name and the client URL. This is because the dispatching of the request is done by the web server directly. It might be a good idea to move this dispatching to our code for better flexibility. This can be easily achieved by @@ -127,18 +127,17 @@ we return a custom 404 page; you are now in control of your website. To access a page, you must now use the ``front.php`` script: -* ``http://example.com/front.php/hello?name=Fabien`` +* ``http://127.0.0.1:4321/front.php/hello?name=Fabien`` -* ``http://example.com/front.php/bye`` +* ``http://127.0.0.1:4321/front.php/bye`` ``/hello`` and ``/bye`` are the page *path*s. .. tip:: - Most web servers like Apache or nginx are able to rewrite the incoming - URLs and remove the front controller script so that your users will be - able to type ``http://example.com/hello?name=Fabien``, which looks much - better. + Most web servers like Apache or nginx are able to rewrite the incoming URLs + and remove the front controller script so that your users will be able to + type ``http://127.0.0.1:4321/hello?name=Fabien``, which looks much better. The trick is the usage of the ``Request::getPathInfo()`` method which returns the path of the Request by removing the front controller script name including @@ -152,8 +151,8 @@ its sub-directories (only if needed -- see above tip). argument is the URL path you want to simulate. Now that the web server always access the same script (``front.php``) for all -our pages, we can secure our code further by moving all other PHP files -outside the web root directory: +pages, we can secure the code further by moving all other PHP files outside the +web root directory: .. code-block:: text @@ -170,14 +169,21 @@ outside the web root directory: Now, configure your web server root directory to point to ``web/`` and all other files won't be accessible from the client anymore. +To test your changes in a browser (``http://localhost:4321/?name=Fabien``), run +the PHP built-in server: + +.. code-block:: sh + + $ php -S 127.0.0.1:4321 -t web/ web/front.php + .. note:: For this new structure to work, you will have to adjust some paths in various PHP files; the changes are left as an exercise for the reader. The last thing that is repeated in each page is the call to ``setContent()``. -We can convert all pages to "templates" by just echoing the content and -calling the ``setContent()`` directly from the front controller script:: +We can convert all pages to "templates" by just echoing the content and calling +the ``setContent()`` directly from the front controller script:: Date: Fri, 24 Oct 2014 10:09:48 +0200 Subject: [PATCH 0066/2667] removed versions when adding Symfony component with Composer --- book/part02.rst | 2 +- book/part04.rst | 2 +- book/part06.rst | 2 +- book/part09.rst | 2 +- book/part12.rst | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/book/part02.rst b/book/part02.rst index f5c007990a7..933fbb6baae 100644 --- a/book/part02.rst +++ b/book/part02.rst @@ -126,7 +126,7 @@ To use this component, add it as a dependency of the project: .. code-block:: sh - $ php composer.phar require symfony/http-foundation 2.5.* + $ php composer.phar require symfony/http-foundation Running this command will also automatically download the Symfony HttpFoundation component and install it under the ``vendor/`` directory. diff --git a/book/part04.rst b/book/part04.rst index 3d370079651..65ddc425315 100644 --- a/book/part04.rst +++ b/book/part04.rst @@ -57,7 +57,7 @@ To support this feature, add the Symfony2 Routing component as a dependency: .. code-block:: sh - $ php composer.phar require symfony/routing 2.5.* + $ php composer.phar require symfony/routing Instead of an array for the URL map, the Routing component relies on a ``RouteCollection`` instance:: diff --git a/book/part06.rst b/book/part06.rst index 148dc839062..3e39bb85782 100644 --- a/book/part06.rst +++ b/book/part06.rst @@ -41,7 +41,7 @@ component: .. code-block:: sh - $ php composer.phar require symfony/http-kernel 2.5.* + $ php composer.phar require symfony/http-kernel The HttpKernel component has many interesting features, but the one we need right now is the *controller resolver*. A controller resolver knows how to diff --git a/book/part09.rst b/book/part09.rst index 7d12e55e973..5a0967205d2 100644 --- a/book/part09.rst +++ b/book/part09.rst @@ -19,7 +19,7 @@ version of this pattern: .. code-block:: sh - $ php composer.phar require symfony/event-dispatcher 2.5.* + $ php composer.phar require symfony/event-dispatcher How does it work? The *dispatcher*, the central object of the event dispatcher system, notifies *listeners* of an *event* dispatched to it. Put another way: diff --git a/book/part12.rst b/book/part12.rst index a1ede9d5833..fbeb189d567 100644 --- a/book/part12.rst +++ b/book/part12.rst @@ -93,7 +93,7 @@ container: .. code-block:: sh - $ php composer.phar require symfony/dependency-injection 2.5.* + $ php composer.phar require symfony/dependency-injection Create a new file to host the dependency injection container configuration:: From 409dba54363f5099c8b9059fedb6e14ce697d719 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 12 Nov 2014 22:05:59 +0100 Subject: [PATCH 0067/2667] move things around --- LICENSE.md | 4 ---- {book => create_framework}/index.rst | 0 {book => create_framework}/part01.rst | 0 {book => create 8000 _framework}/part02.rst | 0 {book => create_framework}/part03.rst | 0 {book => create_framework}/part04.rst | 0 {book => create_framework}/part05.rst | 0 {book => create_framework}/part06.rst | 0 {book => create_framework}/part07.rst | 0 {book => create_framework}/part08.rst | 0 {book => create_framework}/part09.rst | 0 {book => create_framework}/part10.rst | 0 {book => create_framework}/part11.rst | 0 {book => create_framework}/part12.rst | 0 14 files changed, 4 deletions(-) delete mode 100644 LICENSE.md rename {book => create_framework}/index.rst (100%) rename {book => create_framework}/part01.rst (100%) rename {book => create_framework}/part02.rst (100%) rename {book => create_framework}/part03.rst (100%) rename {book => create_framework}/part04.rst (100%) rename {book => create_framework}/part05.rst (100%) rename {book => create_framework}/part06.rst (100%) rename {book => create_framework}/part07.rst (100%) rename {book => create_framework}/part08.rst (100%) rename {book => create_framework}/part09.rst (100%) rename {book => create_framework}/part10.rst (100%) rename {book => create_framework}/part11.rst (100%) rename {book => create_framework}/part12.rst (100%) diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index 176160d1345..00000000000 --- a/LICENSE.md +++ /dev/null @@ -1,4 +0,0 @@ -This work is licensed under a Creative Commons Attribution-Share Alike 3.0 -Unported License. - -http://creativecommons.org/licenses/by-sa/3.0/ diff --git a/book/index.rst b/create_framework/index.rst similarity index 100% rename from book/index.rst rename to create_framework/index.rst diff --git a/book/part01.rst b/create_framework/part01.rst similarity index 100% rename from book/part01.rst rename to create_framework/part01.rst diff --git a/book/part02.rst b/create_framework/part02.rst similarity index 100% rename from book/part02.rst rename to create_framework/part02.rst diff --git a/book/part03.rst b/create_framework/part03.rst similarity index 100% rename from book/part03.rst rename to create_framework/part03.rst diff --git a/book/part04.rst b/create_framework/part04.rst similarity index 100% rename from book/part04.rst rename to create_framework/part04.rst diff --git a/book/part05.rst b/create_framework/part05.rst similarity index 100% rename from book/part05.rst rename to create_framework/part05.rst diff --git a/book/part06.rst b/create_framework/part06.rst similarity index 100% rename from book/part06.rst rename to create_framework/part06.rst diff --git a/book/part07.rst b/create_framework/part07.rst similarity index 100% rename from book/part07.rst rename to create_framework/part07.rst diff --git a/book/part08.rst b/create_framework/part08.rst similarity index 100% rename from book/part08.rst rename to create_framework/part08.rst diff --git a/book/part09.rst b/create_framework/part09.rst similarity index 100% rename from book/part09.rst rename to create_framework/part09.rst diff --git a/book/part10.rst b/create_framework/part10.rst similarity index 100% rename from book/part10.rst rename to create_framework/part10.rst diff --git a/book/part11.rst b/create_framework/part11.rst similarity index 100% rename from book/part11.rst rename to create_framework/part11.rst diff --git a/book/part12.rst b/create_framework/part12.rst similarity index 100% rename from book/part12.rst rename to create_framework/part12.rst From d44e4a2070b685824f76221ca4619f7fff1d9899 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 12 Nov 2014 22:10:17 +0100 Subject: [PATCH 0068/2667] added the new tutorial in the main index --- .../{part01.rst => 01-introduction.rst} | 0 .../{part02.rst => 02-http-foundation.rst} | 0 .../{part03.rst => 03-front-controller.rst} | 0 .../{part04.rst => 04-routing.rst} | 0 .../{part05.rst => 05-templating.rst} | 0 .../{part06.rst => 06-http-kernel.rst} | 0 ...rt07.rst => 07-separation-of-concerns.rst} | 0 .../{part08.rst => 08-unit-testing.rst} | 0 .../{part09.rst => 09-event-dispatcher.rst} | 0 .../{part10.rst => 10-http-kernel.rst} | 0 .../{part11.rst => 11-http-kernel.rst} | 0 ...part12.rst => 12-dependency-injection.rst} | 0 create_framework/index.rst | 24 +++++++++---------- create_framework/map.rst.inc | 12 ++++++++++ index.rst | 12 ++++++++++ 15 files changed, 36 insertions(+), 12 deletions(-) rename create_framework/{part01.rst => 01-introduction.rst} (100%) rename create_framework/{part02.rst => 02-http-foundation.rst} (100%) rename create_framework/{part03.rst => 03-front-controller.rst} (100%) rename create_framework/{part04.rst => 04-routing.rst} (100%) rename create_framework/{part05.rst => 05-templating.rst} (100%) rename create_framework/{part06.rst => 06-http-kernel.rst} (100%) rename create_framework/{part07.rst => 07-separation-of-concerns.rst} (100%) rename create_framework/{part08.rst => 08-unit-testing.rst} (100%) rename create_framework/{part09.rst => 09-event-dispatcher.rst} (100%) rename create_framework/{part10.rst => 10-http-kernel.rst} (100%) rename create_framework/{part11.rst => 11-http-kernel.rst} (100%) rename create_framework/{part12.rst => 12-dependency-injection.rst} (100%) create mode 100644 create_framework/map.rst.inc diff --git a/create_framework/part01.rst b/create_framework/01-introduction.rst similarity index 100% rename from create_framework/part01.rst rename to create_framework/01-introduction.rst diff --git a/create_framework/part02.rst b/create_framework/02-http-foundation.rst similarity index 100% rename from create_framework/part02.rst rename to create_framework/02-http-foundation.rst diff --git a/create_framework/part03.rst b/create_framework/03-front-controller.rst similarity index 100% rename from create_framework/part03.rst rename to create_framework/03-front-controller.rst diff --git a/create_framework/part04.rst b/create_framework/04-routing.rst similarity index 100% rename from create_framework/part04.rst rename to create_framework/04-routing.rst diff --git a/create_framework/part05.rst b/create_framework/05-templating.rst similarity index 100% rename from create_framework/part05.rst rename to create_framework/05-templating.rst diff --git a/create_framework/part06.rst b/create_framework/06-http-kernel.rst similarity index 100% rename from create_framework/part06.rst rename to create_framework/06-http-kernel.rst diff --git a/create_framework/part07.rst b/create_framework/07-separation-of-concerns.rst similarity index 100% rename from create_framework/part07.rst rename to create_framework/07-separation-of-concerns.rst diff --git a/create_framework/part08.rst b/create_framework/08-unit-testing.rst similarity index 100% rename from create_framework/part08.rst rename to create_framework/08-unit-testing.rst diff --git a/create_framework/part09.rst b/create_framework/09-event-dispatcher.rst similarity index 100% rename from create_framework/part09.rst rename to create_framework/09-event-dispatcher.rst diff --git a/create_framework/part10.rst b/create_framework/10-http-kernel.rst similarity index 100% rename from create_framework/part10.rst rename to create_framework/10-http-kernel.rst diff --git a/create_framework/part11.rst b/create_framework/11-http-kernel.rst similarity index 100% rename from create_framework/part11.rst rename to create_framework/11-http-kernel.rst diff --git a/create_framework/part12.rst b/create_framework/12-dependency-injection.rst similarity index 100% rename from create_framework/part12.rst rename to create_framework/12-dependency-injection.rst diff --git a/create_framework/index.rst b/create_framework/index.rst index 120c334d0f1..a7291c1966d 100644 --- a/create_framework/index.rst +++ b/create_framework/index.rst @@ -3,15 +3,15 @@ Create your PHP Framework .. toctree:: - part01 - part02 - part03 - part04 - part05 - part06 - part07 - part08 - part09 - part10 - part11 - part12 + 01-introduction + 02-http-foundation + 03-front-controller + 04-routing + 05-templating + 06-http-kernel + 07-separation-of-concerns + 08-unit-testing + 09-event-dispatcher + 10-http-kernel + 11-http-kernel + 12-dependency-injection diff --git a/create_framework/map.rst.inc b/create_framework/map.rst.inc new file mode 100644 index 00000000000..8e11e1700c3 --- /dev/null +++ b/create_framework/map.rst.inc @@ -0,0 +1,12 @@ +* :doc:`/create_framework/01-introduction` +* :doc:`/create_framework/02-http-foundation` +* :doc:`/create_framework/03-front-controller` +* :doc:`/create_framework/04-routing` +* :doc:`/create_framework/05-templating` +* :doc:`/create_framework/06-http-kernel` +* :doc:`/create_framework/07-separation-of-concerns` +* :doc:`/create_framework/08-unit-testing` +* :doc:`/create_framework/09-event-dispatcher` +* :doc:`/create_framework/10-http-kernel` +* :doc:`/create_framework/11-http-kernel` +* :doc:`/create_framework/12-dependency-injection` diff --git a/index.rst b/index.rst index 2ef2df24f45..cdfd5ff67f8 100644 --- a/index.rst +++ b/index.rst @@ -88,3 +88,15 @@ Contribute to Symfony: contributing/index .. include:: /contributing/map.rst.inc + +Create your Own Framework +------------------------- + +Want to create your own framework based on Symfony? + +.. toctree:: + :hidden: + + create_framework/index + +.. include:: /create_framework/map.rst.inc From bf9c8714f603dccf6c27169e19e610d28927972e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 12 Nov 2014 22:33:39 +0100 Subject: [PATCH 0069/2667] fixed markup --- create_framework/02-http-foundation.rst | 4 +++- create_framework/03-front-controller.rst | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/create_framework/02-http-foundation.rst b/create_framework/02-http-foundation.rst index 933fbb6baae..caee3c592c2 100644 --- a/create_framework/02-http-foundation.rst +++ b/create_framework/02-http-foundation.rst @@ -310,7 +310,7 @@ the wheel. I've almost forgot to talk about one added benefit: using the HttpFoundation component is the start of better interoperability between all frameworks and applications using it (like `Symfony2`_, `Drupal 8`_, `phpBB 4`_, `ezPublish -5`, `Laravel`_, `Silex`_, and `more`_). +5`_, `Laravel`_, `Silex`_, and `more`_). .. _`Twig`: http://twig.sensiolabs.com/ .. _`Symfony2 versus Flat PHP`: http://symfony.com/doc/current/book/from_flat_php_to_symfony2.html @@ -321,6 +321,8 @@ applications using it (like `Symfony2`_, `Drupal 8`_, `phpBB 4`_, `ezPublish .. _`Symfony2`: http://symfony.com/ .. _`Drupal 8`: http://drupal.org/ .. _`phpBB 4`: http://www.phpbb.com/ +.. _`ezPublish 5`: http://ez.no/ +.. _`Laravel`: http://laravel.com/ .. _`Silex`: http://silex.sensiolabs.org/ .. _`Midgard CMS`: http://www.midgard-project.org/ .. _`Zikula`: http://zikula.org/ diff --git a/create_framework/03-front-controller.rst b/create_framework/03-front-controller.rst index e4dc6e69367..b9d101f25bc 100644 --- a/create_framework/03-front-controller.rst +++ b/create_framework/03-front-controller.rst @@ -131,7 +131,7 @@ To access a page, you must now use the ``front.php`` script: * ``http://127.0.0.1:4321/front.php/bye`` -``/hello`` and ``/bye`` are the page *path*s. +``/hello`` and ``/bye`` are the page *paths*. .. tip:: From 13a7170df6ca540798a45d94a8d4a946a722d162 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 20 Nov 2014 17:51:55 +0100 Subject: [PATCH 0070/2667] made some changes to better integrate the tutorial into the current documentation --- create_framework/01-introduction.rst | 43 ++++++++----------- create_framework/02-http-foundation.rst | 36 +++++++--------- create_framework/03-front-controller.rst | 2 +- create_framework/04-routing.rst | 4 +- create_framework/06-http-kernel.rst | 4 +- .../07-separation-of-concerns.rst | 2 +- create_framework/08-unit-testing.rst | 5 +-- create_framework/09-event-dispatcher.rst | 6 +-- create_framework/10-http-kernel.rst | 4 +- create_framework/12-dependency-injection.rst | 8 ++-- 10 files changed, 52 insertions(+), 62 deletions(-) diff --git a/create_framework/01-introduction.rst b/create_framework/01-introduction.rst index acdd97284f1..6891897a854 100644 --- a/create_framework/01-introduction.rst +++ b/create_framework/01-introduction.rst @@ -8,19 +8,14 @@ Instead of using these low-level components, you can use the ready-to-be-used Symfony2 full-stack web framework, which is based on these components... or you can create your very own framework. This book is about the latter. -.. note:: - - If you just want to use the Symfony2 full-stack framework, you'd better - read its official `documentation`_ instead. - Why would you like to create your own framework? ------------------------------------------------ Why would you like to create your own framework in the first place? If you look around, everybody will tell you that it's a bad thing to reinvent the wheel and that you'd better choose an existing framework and forget about -creating your own altogether. Most of the time, they are right but I can think -of a few good reasons to start creating your own framework: +creating your own altogether. Most of the time, they are right but there are +a few good reasons to start creating your own framework: * To learn more about the low level architecture of modern web frameworks in general and about the Symfony2 full-stack framework internals in particular; @@ -37,11 +32,11 @@ of a few good reasons to start creating your own framework: * To prove the world that you can actually create a framework on your own (... but with little effort). -I will gently guide you through the creation of a web framework, one step at a -time. At each step, you will have a fully-working framework that you can use -as is or as a start for your very own. We will start with simple frameworks -and more features will be added with time. Eventually, you will have a -fully-featured full-stack web framework. +This tutorial will gently guide you through the creation of a web framework, +one step at a time. At each step, you will have a fully-working framework that +you can use as is or as a start for your very own. We will start with simple +frameworks and more features will be added with time. Eventually, you will have +a fully-featured full-stack web framework. And of course, each step will be the occasion to learn more about some of the Symfony2 Components. @@ -58,16 +53,16 @@ won't talk about the MVC pattern as the Symfony2 Components are able to create any type of frameworks, not just the ones that follow the MVC architecture. Anyway, if you have a look at the MVC semantics, this book is about how to create the Controller part of a framework. For the Model and the View, it -really depends on your personal taste and I will let you use any existing +really depends on your personal taste and you can use any existing third-party libraries (Doctrine, Propel, or plain-old PDO for the Model; PHP or Twig for the View). -When creating a framework, following the MVC pattern is not the right goal. -The main goal should be the **Separation of Concerns**; I actually think that -this is the only design pattern that you should really care about. The -fundamental principles of the Symfony2 Components are focused on the HTTP -specification. As such, the frameworks that we are going to create should be -more accurately labelled as HTTP frameworks or Request/Response frameworks. +When creating a framework, following the MVC pattern is not the right goal. The +main goal should be the **Separation of Concerns**; this is probably the only +design pattern that you should really care about. The fundamental principles of +the Symfony2 Components are focused on the HTTP specification. As such, the +frameworks that we are going to create should be more accurately labelled as +HTTP frameworks or Request/Response frameworks. Before we start --------------- @@ -89,7 +84,7 @@ classes, how we will reference external dependencies, etc. To store our framework, create a directory somewhere on your machine: -.. code-block:: sh +.. code-block:: bash $ mkdir framework $ cd framework @@ -101,16 +96,16 @@ To install the Symfony2 Components that we need for our framework, we are going to use `Composer`_, a project dependency manager for PHP. If you don't have it yet, `download and install`_ Composer now: -.. code-block:: sh +.. code-block:: bash $ curl -sS https://getcomposer.org/installer | php Then, generate an empty ``composer.json`` file, where Composer will store the framework dependencies: -.. code-block:: sh +.. code-block:: bash - $ php composer.phar init -n + $ composer init -n Our Project ----------- @@ -130,7 +125,7 @@ start with the simplest web application we can think of in PHP:: Use the PHP built-in server to test this great application in a browser (``http://localhost:4321/index.php?name=Fabien``): -.. code-block:: sh +.. code-block:: bash $ php -S 127.0.0.1:4321 diff --git a/create_framework/02-http-foundation.rst b/create_framework/02-http-foundation.rst index caee3c592c2..a1662f7cf75 100644 --- a/create_framework/02-http-foundation.rst +++ b/create_framework/02-http-foundation.rst @@ -1,16 +1,15 @@ The HttpFoundation Component ============================ -Before diving into the framework creation process, I first want to step back -and take a look at why you would like to use a framework instead of keeping -your plain-old PHP applications as is. Why using a framework is actually a -good idea, even for the simplest snippet of code and why creating your -framework on top of the Symfony2 components is better than creating a -framework from scratch. +Before diving into the framework creation process, let's first step back and +let's take a look at why you would like to use a framework instead of keeping +your plain-old PHP applications as is. Why using a framework is actually a good +idea, even for the simplest snippet of code and why creating your framework on +top of the Symfony2 components is better than creating a framework from scratch. .. note:: - I won't talk about the obvious and traditional benefits of using a + We won't talk about the obvious and traditional benefits of using a framework when working on big applications with more than a few developers; the Internet has already plenty of good resources on that topic. @@ -124,9 +123,9 @@ layer. To use this component, add it as a dependency of the project: -.. code-block:: sh +.. code-block:: bash - $ php composer.phar require symfony/http-foundation + $ composer require symfony/http-foundation Running this command will also automatically download the Symfony HttpFoundation component and install it under the ``vendor/`` directory. @@ -270,13 +269,12 @@ chained proxies):: // the client is a known one, so give it some more privilege } -And there is an added benefit: it is *secure* by default. What do I mean by -secure? The ``$_SERVER['HTTP_X_FORWARDED_FOR']`` value cannot be trusted as it -can be manipulated by the end user when there is no proxy. So, if you are -using this code in production without a proxy, it becomes trivially easy to -abuse your system. That's not the case with the ``getClientIp()`` method as -you must explicitly trust your reverse proxies by calling -``setTrustedProxies()``:: +And there is an added benefit: it is *secure* by default. What does it mean? +The ``$_SERVER['HTTP_X_FORWARDED_FOR']`` value cannot be trusted as it can be +manipulated by the end user when there is no proxy. So, if you are using this +code in production without a proxy, it becomes trivially easy to abuse your +system. That's not the case with the ``getClientIp()`` method as you must +explicitly trust your reverse proxies by calling ``setTrustedProxies()``:: `. Believe or not but we have our first framework. You can stop now if you want. Using just the Symfony2 HttpFoundation component already allows you to write @@ -315,8 +313,6 @@ applications using it (like `Symfony2`_, `Drupal 8`_, `phpBB 4`_, `ezPublish .. _`Twig`: http://twig.sensiolabs.com/ .. _`Symfony2 versus Flat PHP`: http://symfony.com/doc/current/book/from_flat_php_to_symfony2.html .. _`HTTP specification`: http://tools.ietf.org/wg/httpbis/ -.. _`API`: http://api.symfony.com/2.0/Symfony/Component/HttpFoundation.html -.. _`documentation`: http://symfony.com/doc/current/components/http_foundation.html .. _`audited`: http://symfony.com/blog/symfony2-security-audit .. _`Symfony2`: http://symfony.com/ .. _`Drupal 8`: http://drupal.org/ diff --git a/create_framework/03-front-controller.rst b/create_framework/03-front-controller.rst index b9d101f25bc..f53a6034b03 100644 --- a/create_framework/03-front-controller.rst +++ b/create_framework/03-front-controller.rst @@ -172,7 +172,7 @@ other files won't be accessible from the client anymore. To test your changes in a browser (``http://localhost:4321/?name=Fabien``), run the PHP built-in server: -.. code-block:: sh +.. code-block:: bash $ php -S 127.0.0.1:4321 -t web/ web/front.php diff --git a/create_framework/04-routing.rst b/create_framework/04-routing.rst index 65ddc425315..7055884efea 100644 --- a/create_framework/04-routing.rst +++ b/create_framework/04-routing.rst @@ -55,9 +55,9 @@ instead of relying on a query string: To support this feature, add the Symfony2 Routing component as a dependency: -.. code-block:: sh +.. code-block:: bash - $ php composer.phar require symfony/routing + $ composer require symfony/routing Instead of an array for the URL map, the Routing component relies on a ``RouteCollection`` instance:: diff --git a/create_framework/06-http-kernel.rst b/create_framework/06-http-kernel.rst index 3e39bb85782..e921a7c6db0 100644 --- a/create_framework/06-http-kernel.rst +++ b/create_framework/06-http-kernel.rst @@ -39,9 +39,9 @@ instantiated. To solve this issue, and a bunch more, let's install and use the HttpKernel component: -.. code-block:: sh +.. code-block:: bash - $ php composer.phar require symfony/http-kernel + $ composer require symfony/http-kernel The HttpKernel component has many interesting features, but the one we need right now is the *controller resolver*. A controller resolver knows how to diff --git a/create_framework/07-separation-of-concerns.rst b/create_framework/07-separation-of-concerns.rst index f3675809d0e..29b4e749638 100644 --- a/create_framework/07-separation-of-concerns.rst +++ b/create_framework/07-separation-of-concerns.rst @@ -98,7 +98,7 @@ be autoloaded, update the ``composer.json`` file: .. note:: - For the Composer autoloader to be updated, run ``php composer.phar update``. + For the Composer autoloader to be updated, run ``composer update``. Move the controller to ``Calendar\\Controller\\LeapYearController``:: diff --git a/create_framework/08-unit-testing.rst b/create_framework/08-unit-testing.rst index e61da949a3e..e0d2ba1d6ff 100644 --- a/create_framework/08-unit-testing.rst +++ b/create_framework/08-unit-testing.rst @@ -119,9 +119,8 @@ Executing this test is as simple as running ``phpunit`` from the .. note:: - I do not explain how the code works in details as this is not the goal of - this book, but if you don't understand what the hell is going on, I highly - recommend you to read PHPUnit documentation on `test doubles`_. + If you don't understand what the hell is going on in the code, read + PHPUnit documentation on `test doubles`_. After the test ran, you should see a green bar. If not, you have a bug either in the test or in the framework code! diff --git a/create_framework/09-event-dispatcher.rst b/create_framework/09-event-dispatcher.rst index 5a0967205d2..8cf8b968250 100644 --- a/create_framework/09-event-dispatcher.rst +++ b/create_framework/09-event-dispatcher.rst @@ -17,9 +17,9 @@ pattern, the *Observer*, to allow any kind of behaviors to be attached to our framework; the Symfony2 EventDispatcher Component implements a lightweight version of this pattern: -.. code-block:: sh +.. code-block:: bash - $ php composer.phar require symfony/event-dispatcher + $ composer require symfony/event-dispatcher How does it work? The *dispatcher*, the central object of the event dispatcher system, notifies *listeners* of an *event* dispatched to it. Put another way: @@ -165,7 +165,7 @@ type is HTML (these conditions demonstrate the ease of manipulating the Request and Response data from your code). So far so good, but let's add another listener on the same event. Let's say -that I want to set the ``Content-Length`` of the Response if it is not already +that we want to set the ``Content-Length`` of the Response if it is not already set:: $dispatcher->addListener('response', function (Simplex\ResponseEvent $event) { diff --git a/create_framework/10-http-kernel.rst b/create_framework/10-http-kernel.rst index c6828ca7907..3c236d21a65 100644 --- a/create_framework/10-http-kernel.rst +++ b/create_framework/10-http-kernel.rst @@ -99,8 +99,8 @@ content and check that the number only changes every 10 seconds:: Using HTTP cache headers to manage your application cache is very powerful and allows you to tune finely your caching strategy as you can use both the expiration and the validation models of the HTTP specification. If you are not -comfortable with these concepts, I highly recommend you to read the `HTTP -caching`_ chapter of the Symfony2 documentation. +comfortable with these concepts, read the `HTTP caching`_ chapter of the +Symfony2 documentation. The Response class contains many other methods that let you configure the HTTP cache very easily. One of the most powerful is ``setCache()`` as it diff --git a/create_framework/12-dependency-injection.rst b/create_framework/12-dependency-injection.rst index fbeb189d567..9ebc1d0181c 100644 --- a/create_framework/12-dependency-injection.rst +++ b/create_framework/12-dependency-injection.rst @@ -91,9 +91,9 @@ front controller? As you might expect, there is a solution. We can solve all these issues and some more by using the Symfony2 dependency injection container: -.. code-block:: sh +.. code-block:: bash - $ php composer.phar require symfony/dependency-injection + $ composer require symfony/dependency-injection Create a new file to host the dependency injection container configuration:: @@ -243,8 +243,8 @@ in great details, but hopefully it gives you enough information to get started on your own and to better understand how the Symfony2 framework works internally. -If you want to learn more, I highly recommend you to read the source code of -the `Silex`_ micro-framework, and especially its `Application`_ class. +If you want to learn more, read the source code of the `Silex`_ +micro-framework, and especially its `Application`_ class. Have fun! From 126bcef1e7f8d6695e0340bfbac4beca649b8e8a Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 5 Feb 2015 17:25:43 +0100 Subject: [PATCH 0071/2667] removed external references alignement --- create_framework/01-introduction.rst | 8 +++--- create_framework/02-http-foundation.rst | 28 ++++++++++---------- create_framework/05-templating.rst | 2 +- create_framework/06-http-kernel.rst | 4 +-- create_framework/08-unit-testing.rst | 4 +-- create_framework/10-http-kernel.rst | 4 +-- create_framework/12-dependency-injection.rst | 4 +-- 7 files changed, 27 insertions(+), 27 deletions(-) diff --git a/create_framework/01-introduction.rst b/create_framework/01-introduction.rst index 6891897a854..5b31758a01e 100644 --- a/create_framework/01-introduction.rst +++ b/create_framework/01-introduction.rst @@ -132,8 +132,8 @@ Use the PHP built-in server to test this great application in a browser In the next chapter, we are going to introduce the HttpFoundation Component and see what it brings us. -.. _`Symfony2`: http://symfony.com/ -.. _`documentation`: http://symfony.com/doc -.. _`Silex`: http://silex.sensiolabs.org/ -.. _`Composer`: http://packagist.org/about-composer +.. _`Symfony2`: http://symfony.com/ +.. _`documentation`: http://symfony.com/doc +.. _`Silex`: http://silex.sensiolabs.org/ +.. _`Composer`: http://packagist.org/about-composer .. _`download and install`: https://getcomposer.org/doc/01-basic-usage.md diff --git a/create_framework/02-http-foundation.rst b/create_framework/02-http-foundation.rst index a1662f7cf75..42e32112121 100644 --- a/create_framework/02-http-foundation.rst +++ b/create_framework/02-http-foundation.rst @@ -310,18 +310,18 @@ component is the start of better interoperability between all frameworks and applications using it (like `Symfony2`_, `Drupal 8`_, `phpBB 4`_, `ezPublish 5`_, `Laravel`_, `Silex`_, and `more`_). -.. _`Twig`: http://twig.sensiolabs.com/ +.. _`Twig`: http://twig.sensiolabs.com/ .. _`Symfony2 versus Flat PHP`: http://symfony.com/doc/current/book/from_flat_php_to_symfony2.html -.. _`HTTP specification`: http://tools.ietf.org/wg/httpbis/ -.. _`audited`: http://symfony.com/blog/symfony2-security-audit -.. _`Symfony2`: http://symfony.com/ -.. _`Drupal 8`: http://drupal.org/ -.. _`phpBB 4`: http://www.phpbb.com/ -.. _`ezPublish 5`: http://ez.no/ -.. _`Laravel`: http://laravel.com/ -.. _`Silex`: http://silex.sensiolabs.org/ -.. _`Midgard CMS`: http://www.midgard-project.org/ -.. _`Zikula`: http://zikula.org/ -.. _`autoloaded`: http://php.net/autoload -.. _`PSR-0`: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md -.. _`more`: http://symfony.com/components/HttpFoundation +.. _`HTTP specification`: http://tools.ietf.org/wg/httpbis/ +.. _`audited`: http://symfony.com/blog/symfony2-security-audit +.. _`Symfony2`: http://symfony.com/ +.. _`Drupal 8`: http://drupal.org/ +.. _`phpBB 4`: http://www.phpbb.com/ +.. _`ezPublish 5`: http://ez.no/ +.. _`Laravel`: http://laravel.com/ +.. _`Silex`: http://silex.sensiolabs.org/ +.. _`Midgard CMS`: http://www.midgard-project.org/ +.. _`Zikula`: http://zikula.org/ +.. _`autoloaded`: http://php.net/autoload +.. _`PSR-0`: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md +.. _`more`: http://symfony.com/components/HttpFoundation diff --git a/create_framework/05-templating.rst b/create_framework/05-templating.rst index f2494c332e7..45440edf055 100644 --- a/create_framework/05-templating.rst +++ b/create_framework/05-templating.rst @@ -187,4 +187,4 @@ probably all you need to create simple websites like those fancy one-page `websites`_ and hopefully a few others. .. _`callbacks`: http://php.net/callback#language.types.callback -.. _`websites`: http://kottke.org/08/02/single-serving-sites +.. _`websites`: http://kottke.org/08/02/single-serving-sites diff --git a/create_framework/06-http-kernel.rst b/create_framework/06-http-kernel.rst index e921a7c6db0..3039abcf4da 100644 --- a/create_framework/06-http-kernel.rst +++ b/create_framework/06-http-kernel.rst @@ -195,6 +195,6 @@ Let's conclude with the new version of our framework:: Think about it once more: our framework is more robust and more flexible than ever and it still has less than 40 lines of code. -.. _`reflection`: http://php.net/reflection -.. _`FrameworkExtraBundle`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annota 8000 tions/converters.html +.. _`reflection`: http://php.net/reflection +.. _`FrameworkExtraBundle`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html .. _`controllers as services`: http://symfony.com/doc/current/cookbook/controller/service.html diff --git a/create_framework/08-unit-testing.rst b/create_framework/08-unit-testing.rst index e0d2ba1d6ff..dc1c75715b9 100644 --- a/create_framework/08-unit-testing.rst +++ b/create_framework/08-unit-testing.rst @@ -189,6 +189,6 @@ Symfony2 code. Now that we are confident (again) about the code we have written, we can safely think about the next batch of features we want to add to our framework. -.. _`PHPUnit`: http://www.phpunit.de/manual/current/en/index.html +.. _`PHPUnit`: http://www.phpunit.de/manual/current/en/index.html .. _`test doubles`: http://www.phpunit.de/manual/current/en/test-doubles.html -.. _`XDebug`: http://xdebug.org/ +.. _`XDebug`: http://xdebug.org/ diff --git a/create_framework/10-http-kernel.rst b/create_framework/10-http-kernel.rst index 3c236d21a65..a4afdd3754b 100644 --- a/create_framework/10-http-kernel.rst +++ b/create_framework/10-http-kernel.rst @@ -188,5 +188,5 @@ the many features built into the HttpKernel component; HTTP caching being just one of them but an important one as it can make your applications fly! .. _`HTTP caching`: http://symfony.com/doc/current/book/http_cache.html -.. _`ESI`: http://en.wikipedia.org/wiki/Edge_Side_Includes -.. _`Varnish`: https://www.varnish-cache.org/ +.. _`ESI`: http://en.wikipedia.org/wiki/Edge_Side_Includes +.. _`Varnish`: https://www.varnish-cache.org/ diff --git a/create_framework/12-dependency-injection.rst b/create_framework/12-dependency-injection.rst index 9ebc1d0181c..000e49b9aea 100644 --- a/create_framework/12-dependency-injection.rst +++ b/create_framework/12-dependency-injection.rst @@ -248,6 +248,6 @@ micro-framework, and especially its `Application`_ class. Have fun! -.. _`Pimple`: https://github.com/fabpot/Pimple -.. _`Silex`: https://silex.sensiolabs.org/ +.. _`Pimple`: https://github.com/fabpot/Pimple +.. _`Silex`: https://silex.sensiolabs.org/ .. _`Application`: https://github.com/fabpot/Silex/blob/master/src/Silex/Application.php From 5bc5969903e755af3a6811af3e1c63e1ee02b33c Mon Sep 17 00:00:00 2001 From: azielinski Date: Mon, 2 Mar 2015 03:17:33 +0100 Subject: [PATCH 0072/2667] improved description of choice_list option of Choice form type --- reference/forms/types/choice.rst | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index a5757116817..22b69947a4e 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -111,7 +111,7 @@ The ``choice_list`` option must be an instance of the ``ChoiceListInterface``. For more advanced cases, a custom class that implements the interface can be created to supply the choices. -With this option you can also allow float values to be selected as data. +With this option you can also allow float values to be selected as data. For example: .. code-block:: php @@ -119,9 +119,19 @@ With this option you can also allow float values to be selected as data. // ... $builder->add('status', 'choice', array( - 'choice_list' => new ChoiceList(array(1, 0.5), array('Full', 'Half')) + 'choice_list' => new ChoiceList(array(1, 0.5, 0.1), array('Full', 'Half', 'Almost empty')) )); +The ``status`` field created by the code above will be rendered as: + +.. code-block:: html + + + .. include:: /reference/forms/types/options/empty_value.rst.inc .. include:: /reference/forms/types/options/expanded.rst.inc From 86da33885b25c4dc072d44cc17d234b42efe0b59 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 10 Apr 2015 17:38:10 +0200 Subject: [PATCH 0073/2667] Proposed a new article about using pure PHP libraries with Assetic --- cookbook/frontend/assetic_php.rst | 156 ++++++++++++++++++++++++++++++ cookbook/index.rst | 1 + cookbook/map.rst.inc | 5 + 3 files changed, 162 insertions(+) create mode 100644 cookbook/frontend/assetic_php.rst diff --git a/cookbook/frontend/assetic_php.rst b/cookbook/frontend/assetic_php.rst new file mode 100644 index 00000000000..5d19ffe8e42 --- /dev/null +++ b/cookbook/frontend/assetic_php.rst @@ -0,0 +1,156 @@ +.. index:: + single: Front-end; Assetic, Bootstrap + +Combining, Compiling and Minimizing Web Assets with Pure PHP Libraries +====================================================================== + +The official Symfony Best Practices recommend to use Assetic to +:doc:`manage web assets `, unless you are +comfortable with JavaScript-based frontend tools. + +Even if those JavaScript-based solutions are the most suitable ones from a +technical point of view, using pure PHP alternative libraries can be useful in +some scenarios: + +* If you can't install or use ``npm`` and the other JavaScript solutions; +* If you prefer to limit the amount of different technologies used in your + applications; +* If you want to simplify application deployment. + +In this article you'll learn how to combine and minimize CSS and JavaScript files +and how to compile Sass SCSS files using pure PHP libraries with Assetic. + +Installing the Third-Party Compression Libraries +------------------------------------------------ + +Assetic includes a lot of ready-to-use filters but it doesn't include their +associated libraries. Therefore, before enabling the filters used in this article +you must install two libraries. Open a command console, browse to your project +directory and execute the following commands: + +.. code-block:: bash + + $ composer require leafo/scssphp + $ composer require patchwork/jsqueeze:"~1.0" + +It's very important to maintain the ``~1.0`` version constraint for the ``jsqueeze`` +dependency because the most recent stable version is not compatible with Assetic. + +Organizing Your Web Asset Files +------------------------------- + +This example shows the very common scenario of using the Bootstrap framework, the +jQuery library, the FontAwesome icon font and some regular CSS and JavaScript +application files (called ``main.css`` and ``main.js``). The recommended directory +structure for this set-up is the following: + +.. code-block:: text + + web/assets/ + ├── css + │   ├── main.css + │   └── code-highlight.css + ├── js + │   ├── bootstrap.js + │   ├── jquery.js + │   └── main.js + └── scss + ├── bootstrap + │   ├── _alerts.scss + │   ├── ... + │   ├── _variables.scss + │   ├── _wells.scss + │   └── mixins + │   ├── _alerts.scss + │   ├── ... + │   └── _vendor-prefixes.scss + ├── bootstrap.scss + ├── font-awesome + │   ├── _animated.scss + │   ├── ... + │   └── _variables.scss + └── font-awesome.scss + +Combining and Minimizing CSS Files and Compiling SCSS Files +----------------------------------------------------------- + +First, configure a new ``scssphp`` Assetic filter as follows: + +.. code-block:: yaml + + # app/config/config.yml + assetic: + filters: + scssphp: + formatter: "Leafo\\ScssPhp\\Formatter\\Compressed" + # ... + +The value of the ``formatter`` option is the fully qualified class name of the +formatter used by the filter to produce the compiled CSS file. Using the +compressed formatter allows to minimize the resulting file, no matter if the +original files are regular CSS files or SCSS files. + +Then, update the code of your Twig template to add the ``{% stylesheets %}`` tag +defined by Assetic: + +.. code-block:: jinja+html + + + + + + + {% stylesheets filter="?scssphp" output="css/app.css" + "assets/scss/bootstrap.scss" + "assets/scss/font-awesome.scss" + "assets/css/*.css" + %} + + {% endstylesheets %} + +This simple configuration compiles the SCSS files into regular CSS files, combines +all of them, minimizes the contents and saves the output in the ``web/css/app.css`` +file, which is the one that is served to your visitors. + +The leading ``?`` character in the ``scssphp`` filter name indicates that it must +be applied only when the ``debug`` mode is disabled in the application, which +usually occurs in the production environment. + +Combining and Minimizing JavaScript Files +----------------------------------------- + +First, configure a new ``jsqueeze`` Assetic filter as follows: + +.. code-block:: yaml + + # app/config/config.yml + assetic: + filters: + jsqueeze: ~ + # ... + +Then, update the code of your Twig template to add the ``{% javascripts %}`` tag +defined by Assetic: + +.. code-block:: jinja+html + + + + {% javascripts filter="?jsqueeze" output="js/app.js" + "assets/js/jquery.js" + "assets/js/bootstrap.js" + "assets/js/main.js" + %} + + {% endjavascripts %} + + + + +This simple configuration combines all the JavaScript files, minimizes the contents +and saves the output in the ``web/js/app.js`` file, which is the one that is +served to your visitors. + +Similarly to the ``scssphp`` filter, the leading ``?`` character in the ``jsqueeze`` +filter name indicates that it must be applied only when the ``debug`` mode is +disabled in the application, which usually occurs in the production environment. diff --git a/cookbook/index.rst b/cookbook/index.rst index 03d29060c7f..7ad4360e21b 100644 --- a/cookbook/index.rst +++ b/cookbook/index.rst @@ -17,6 +17,7 @@ The Cookbook email/index event_dispatcher/index form/index + frontend/index logging/index profiler/index request/index diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 6c8e5d59d89..40798c22a05 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -107,6 +107,11 @@ * (validation) :doc:`/cookbook/validation/custom_constraint` * (doctrine) :doc:`/cookbook/doctrine/file_uploads` + +* :doc:`/cookbook/frontend/index` + + * :doc:`/cookbook/frontend/assetic_php` + * :doc:`/cookbook/logging/index` * :doc:`/cookbook/logging/monolog` From 798b5b5e549c5561f374710e85491047430cb72a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 10 Apr 2015 20:04:36 +0200 Subject: [PATCH 0074/2667] Added the missin index file --- cookbook/frontend/index.rst | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 cookbook/frontend/index.rst diff --git a/cookbook/frontend/index.rst b/cookbook/frontend/index.rst new file mode 100644 index 00000000000..8e72b465c59 --- /dev/null +++ b/cookbook/frontend/index.rst @@ -0,0 +1,7 @@ +Form +==== + +.. toctree:: + :maxdepth: 2 + + assetic_php From 14d03462ad9d89317a57c8a5440e4e465b703839 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 10 Apr 2015 20:15:31 +0200 Subject: [PATCH 0075/2667] Fixed the Twig lexer name --- cookbook/frontend/assetic_php.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/frontend/assetic_php.rst b/cookbook/frontend/assetic_php.rst index 5d19ffe8e42..85c7047201c 100644 --- a/cookbook/frontend/assetic_php.rst +++ b/cookbook/frontend/assetic_php.rst @@ -93,7 +93,7 @@ original files are regular CSS files or SCSS files. Then, update the code of your Twig template to add the ``{% stylesheets %}`` tag defined by Assetic: -.. code-block:: jinja+html +.. code-block:: html+jinja @@ -132,7 +132,7 @@ First, configure a new ``jsqueeze`` Assetic filter as follows: Then, update the code of your Twig template to add the ``{% javascripts %}`` tag defined by Assetic: -.. code-block:: jinja+html +.. code-block:: html+jinja From 044cd73a66200ce57aec3de77083bc41fac49844 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 10 Apr 2015 20:25:11 +0200 Subject: [PATCH 0076/2667] The file extension is not needed --- cookbook/frontend/assetic_php.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/frontend/assetic_php.rst b/cookbook/frontend/assetic_php.rst index 85c7047201c..fd2f6c5202f 100644 --- a/cookbook/frontend/assetic_php.rst +++ b/cookbook/frontend/assetic_php.rst @@ -5,7 +5,7 @@ Combining, Compiling and Minimizing Web Assets with Pure PHP Libraries ====================================================================== The official Symfony Best Practices recommend to use Assetic to -:doc:`manage web assets `, unless you are +:doc:`manage web assets `, unless you are comfortable with JavaScript-based frontend tools. Even if those JavaScript-based solutions are the most suitable ones from a From cc5b630442f53d590d734a12b8ce608ab133d0d7 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 10 Apr 2015 20:35:16 +0200 Subject: [PATCH 0077/2667] Reworded some wrong statements --- cookbook/frontend/assetic_php.rst | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/cookbook/frontend/assetic_php.rst b/cookbook/frontend/assetic_php.rst index fd2f6c5202f..2856ae5a81e 100644 --- a/cookbook/frontend/assetic_php.rst +++ b/cookbook/frontend/assetic_php.rst @@ -100,7 +100,7 @@ defined by Assetic: - {% stylesheets filter="?scssphp" output="css/app.css" + {% stylesheets filter="scssphp" output="css/app.css" "assets/scss/bootstrap.scss" "assets/scss/font-awesome.scss" "assets/css/*.css" @@ -112,10 +112,6 @@ This simple configuration compiles the SCSS files into regular CSS files, combin all of them, minimizes the contents and saves the output in the ``web/css/app.css`` file, which is the one that is served to your visitors. -The leading ``?`` character in the ``scssphp`` filter name indicates that it must -be applied only when the ``debug`` mode is disabled in the application, which -usually occurs in the production environment. - Combining and Minimizing JavaScript Files ----------------------------------------- @@ -151,6 +147,6 @@ This simple configuration combines all the JavaScript files, minimizes the conte and saves the output in the ``web/js/app.js`` file, which is the one that is served to your visitors. -Similarly to the ``scssphp`` filter, the leading ``?`` character in the ``jsqueeze`` -filter name indicates that it must be applied only when the ``debug`` mode is -disabled in the application, which usually occurs in the production environment. +The leading ``?`` character in the ``jsqueeze`` filter name indicates that it must +be applied only when the ``debug`` mode is disabled in the application, which +usually occurs in the production environment. From 1f6e16cc324e0fe140d84c08c6a1e8091ddb593f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 13 Apr 2015 16:50:15 +0200 Subject: [PATCH 0078/2667] Minor fixes --- cookbook/frontend/assetic_php.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cookbook/frontend/assetic_php.rst b/cookbook/frontend/assetic_php.rst index 2856ae5a81e..9340d3f832f 100644 --- a/cookbook/frontend/assetic_php.rst +++ b/cookbook/frontend/assetic_php.rst @@ -1,8 +1,8 @@ .. index:: single: Front-end; Assetic, Bootstrap -Combining, Compiling and Minimizing Web Assets with Pure PHP Libraries -====================================================================== +Combining, Compiling and Minimizing Web Assets with PHP Libraries +================================================================= The official Symfony Best Practices recommend to use Assetic to :doc:`manage web assets `, unless you are @@ -18,7 +18,7 @@ some scenarios: * If you want to simplify application deployment. In this article you'll learn how to combine and minimize CSS and JavaScript files -and how to compile Sass SCSS files using pure PHP libraries with Assetic. +and how to compile Sass SCSS files using PHP only libraries with Assetic. Installing the Third-Party Compression Libraries ------------------------------------------------ @@ -40,7 +40,7 @@ Organizing Your Web Asset Files ------------------------------- This example shows the very common scenario of using the Bootstrap framework, the -jQuery library, the FontAwesome icon font and some regular CSS and JavaScript +jQuery library, the FontAwesome icon fonts and some regular CSS and JavaScript application files (called ``main.css`` and ``main.js``). The recommended directory structure for this set-up is the following: From 3ea9c86165eb353088c3fda67f9622f3b9f5feed Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 14 Apr 2015 13:14:03 +0200 Subject: [PATCH 0079/2667] Fixed some typos and grammar issues --- cookbook/frontend/assetic_php.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cookbook/frontend/assetic_php.rst b/cookbook/frontend/assetic_php.rst index 9340d3f832f..f7bf02e69f2 100644 --- a/cookbook/frontend/assetic_php.rst +++ b/cookbook/frontend/assetic_php.rst @@ -6,7 +6,7 @@ Combining, Compiling and Minimizing Web Assets with PHP Libraries The official Symfony Best Practices recommend to use Assetic to :doc:`manage web assets `, unless you are -comfortable with JavaScript-based frontend tools. +comfortable with JavaScript-based front-end tools. Even if those JavaScript-based solutions are the most suitable ones from a technical point of view, using pure PHP alternative libraries can be useful in @@ -17,14 +17,14 @@ some scenarios: applications; * If you want to simplify application deployment. -In this article you'll learn how to combine and minimize CSS and JavaScript files -and how to compile Sass SCSS files using PHP only libraries with Assetic. +In this article, you'll learn how to combine and minimize CSS and JavaScript files +and how to compile Sass files using PHP only libraries with Assetic. Installing the Third-Party Compression Libraries ------------------------------------------------ -Assetic includes a lot of ready-to-use filters but it doesn't include their -associated libraries. Therefore, before enabling the filters used in this article +Assetic includes a lot of ready-to-use filters, but it doesn't include their +associated libraries. Therefore, before enabling the filters used in this article, you must install two libraries. Open a command console, browse to your project directory and execute the following commands: From bc6ffbe307071a5aa6998daea6c1b3ee1d3af602 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 14 Apr 2015 13:26:33 +0200 Subject: [PATCH 0080/2667] Rewords and code improvements --- cookbook/frontend/assetic_php.rst | 48 ++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/cookbook/frontend/assetic_php.rst b/cookbook/frontend/assetic_php.rst index f7bf02e69f2..b2751d464e7 100644 --- a/cookbook/frontend/assetic_php.rst +++ b/cookbook/frontend/assetic_php.rst @@ -36,10 +36,10 @@ directory and execute the following commands: It's very important to maintain the ``~1.0`` version constraint for the ``jsqueeze`` dependency because the most recent stable version is not compatible with Assetic. -Organizing Your Web Asset Files +Organizing your Web Asset Files ------------------------------- -This example shows the very common scenario of using the Bootstrap framework, the +This example shows the common scenario of using the Bootstrap framework, the jQuery library, the FontAwesome icon fonts and some regular CSS and JavaScript application files (called ``main.css`` and ``main.js``). The recommended directory structure for this set-up is the following: @@ -95,6 +95,7 @@ defined by Assetic: .. code-block:: html+jinja + {# app/Resources/views/base.html.twig #} @@ -108,22 +109,47 @@ defined by Assetic: {% endstylesheets %} -This simple configuration compiles the SCSS files into regular CSS files, combines -all of them, minimizes the contents and saves the output in the ``web/css/app.css`` -file, which is the one that is served to your visitors. +This simple configuration compiles, combines and minifies the SCSS files into a +regular CSS file that's put in ``web/css/app.css``. This is the only CSS file +which will be served to your visitors. Combining and Minimizing JavaScript Files ----------------------------------------- First, configure a new ``jsqueeze`` Assetic filter as follows: -.. code-block:: yaml +.. configuration-block:: - # app/config/config.yml - assetic: - filters: - jsqueeze: ~ - # ... + .. code-block:: yaml + + # app/config/config.yml + assetic: + filters: + jsqueeze: ~ + # ... + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('assetic', array( + 'filters' => array( + 'jsqueeze' => null, + // ... + ), + )); Then, update the code of your Twig template to add the ``{% javascripts %}`` tag defined by Assetic: From 85e6a543c0676c33988f840a4e8be7b5637374aa Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 14 Apr 2015 13:27:51 +0200 Subject: [PATCH 0081/2667] Added more configuration formats --- cookbook/frontend/assetic_php.rst | 43 +++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/cookbook/frontend/assetic_php.rst b/cookbook/frontend/assetic_php.rst index b2751d464e7..7c24d19fad2 100644 --- a/cookbook/frontend/assetic_php.rst +++ b/cookbook/frontend/assetic_php.rst @@ -76,14 +76,41 @@ Combining and Minimizing CSS Files and Compiling SCSS Files First, configure a new ``scssphp`` Assetic filter as follows: -.. code-block:: yaml - - # app/config/config.yml - assetic: - filters: - scssphp: - formatter: "Leafo\\ScssPhp\\Formatter\\Compressed" - # ... +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + assetic: + filters: + scssphp: + formatter: "Leafo\\ScssPhp\\Formatter\\Compressed" + # ... + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('assetic', array( + 'filters' => array( + 'scssphp' => array( + 'formatter' => 'Leafo\ScssPhp\Formatter\Compressed', + ), + // ... + ), + )); The value of the ``formatter`` option is the fully qualified class name of the formatter used by the filter to produce the compiled CSS file. Using the From 07087dd57b123f2b49131254ba614b373eafa228 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 14 Apr 2015 13:28:51 +0200 Subject: [PATCH 0082/2667] Avoid the ugly double back slashes --- cookbook/frontend/assetic_php.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/frontend/assetic_php.rst b/cookbook/frontend/assetic_php.rst index 7c24d19fad2..6124e50f960 100644 --- a/cookbook/frontend/assetic_php.rst +++ b/cookbook/frontend/assetic_php.rst @@ -84,7 +84,7 @@ First, configure a new ``scssphp`` Assetic filter as follows: assetic: filters: scssphp: - formatter: "Leafo\\ScssPhp\\Formatter\\Compressed" + formatter: 'Leafo\ScssPhp\Formatter\Compressed' # ... .. code-block:: xml From 4b8b3fbc501f982e6b29cb92ffc88e130bf202e5 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 14 Apr 2015 13:30:53 +0200 Subject: [PATCH 0083/2667] Moved the article back to the Assetic section --- cookbook/assetic/index.rst | 1 + cookbook/{frontend/assetic_php.rst => assetic/php.rst} | 0 cookbook/frontend/index.rst | 7 ------- cookbook/index.rst | 1 - cookbook/map.rst.inc | 6 +----- 5 files changed, 2 insertions(+), 13 deletions(-) rename cookbook/{frontend/assetic_php.rst => assetic/php.rst} (100%) delete mode 100644 cookbook/frontend/index.rst diff --git a/cookbook/assetic/index.rst b/cookbook/assetic/index.rst index a4b084c22f0..c37efdc16ee 100644 --- a/cookbook/assetic/index.rst +++ b/cookbook/assetic/index.rst @@ -5,6 +5,7 @@ Assetic :maxdepth: 2 asset_management + php uglifyjs yuicompressor jpeg_optimize diff --git a/cookbook/frontend/assetic_php.rst b/cookbook/assetic/php.rst similarity index 100% rename from cookbook/frontend/assetic_php.rst rename to cookbook/assetic/php.rst diff --git a/cookbook/frontend/index.rst b/cookbook/frontend/index.rst deleted file mode 100644 index 8e72b465c59..00000000000 --- a/cookbook/frontend/index.rst +++ /dev/null @@ -1,7 +0,0 @@ -Form -==== - -.. toctree:: - :maxdepth: 2 - - assetic_php diff --git a/cookbook/index.rst b/cookbook/index.rst index 7ad4360e21b..03d29060c7f 100644 --- a/cookbook/index.rst +++ b/cookbook/index.rst @@ -17,7 +17,6 @@ The Cookbook email/index event_dispatcher/index form/index - frontend/index logging/index profiler/index request/index diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 40798c22a05..448b2868e6d 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -1,6 +1,7 @@ * :doc:`/cookbook/assetic/index` * :doc:`/cookbook/assetic/asset_management` + * :doc:`/cookbook/assetic/php` * :doc:`/cookbook/assetic/uglifyjs` * :doc:`/cookbook/assetic/yuicompressor` * :doc:`/cookbook/assetic/jpeg_optimize` @@ -107,11 +108,6 @@ * (validation) :doc:`/cookbook/validation/custom_constraint` * (doctrine) :doc:`/cookbook/doctrine/file_uploads` - -* :doc:`/cookbook/frontend/index` - - * :doc:`/cookbook/frontend/assetic_php` - * :doc:`/cookbook/logging/index` * :doc:`/cookbook/logging/monolog` From f5c4d930559f740107934f43129c5ef8d5d35e61 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 14 Apr 2015 13:32:31 +0200 Subject: [PATCH 0084/2667] Fixed an indentation problem --- cookbook/assetic/php.rst | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/cookbook/assetic/php.rst b/cookbook/assetic/php.rst index 6124e50f960..b4ce591079e 100644 --- a/cookbook/assetic/php.rst +++ b/cookbook/assetic/php.rst @@ -89,28 +89,28 @@ First, configure a new ``scssphp`` Assetic filter as follows: .. code-block:: xml - - - + + + - - - - - + + + + + .. code-block:: php - // app/config/config.php - $container->loadFromExtension('assetic', array( - 'filters' => array( - 'scssphp' => array( - 'formatter' => 'Leafo\ScssPhp\Formatter\Compressed', - ), - // ... - ), - )); + // app/config/config.php + $container->loadFromExtension('assetic', array( + 'filters' => array( + 'scssphp' => array( + 'formatter' => 'Leafo\ScssPhp\Formatter\Compressed', + ), + // ... + ), + )); The value of the ``formatter`` option is the fully qualified class name of the formatter used by the filter to produce the compiled CSS file. Using the From c47ec883dc4369d59dd45dd1109a301e62b7f19f Mon Sep 17 00:00:00 2001 From: Jakub Zalas Date: Tue, 12 May 2015 17:32:06 +0100 Subject: [PATCH 0085/2667] [DomCrawler] Warn users of older PHP versions Crawler might not decode html entities properly. --- components/dom_crawler.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/components/dom_crawler.rst b/components/dom_crawler.rst index 4a4da289d4f..55b7a55be45 100644 --- a/components/dom_crawler.rst +++ b/components/dom_crawler.rst @@ -231,6 +231,7 @@ and :phpclass:`DOMNode` objects: $html = $crawler->html(); The ``html`` method is new in Symfony 2.3. + In PHP < 5.3.6 it might return html entities which are not properly decoded. Links ~~~~~ From 051a23f141798d84e2cc9040803c6cff9697a85b Mon Sep 17 00:00:00 2001 From: daFish Date: Thu, 29 Nov 2012 16:25:30 +0100 Subject: [PATCH 0086/2667] document how to render custom collection prototypes --- cookbook/form/form_collections.rst | 70 ++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/cookbook/form/form_collections.rst b/cookbook/form/form_collections.rst index 866a1d1b024..8511844e855 100644 --- a/cookbook/form/form_collections.rst +++ b/cookbook/form/form_collections.rst @@ -729,5 +729,75 @@ the relationship between the removed ``Tag`` and ``Task`` object. updated (whether you're adding new tags or removing existing tags) on each Tag object itself. +.. _cookbook-form-collections-custom-prototype: + +Rendering a Custom Prototype +---------------------------- + +Most of the time the provided prototype will be sufficient for your needs +and does not need to be changed. But if you are in the situation were you +need to have a complete custom prototype, you can render it yourself. + +The Form component automatically looks for a block whose name follows a certain +schema to decide how to render each entry of the form type collection. For +example, if your form field is named ``tasks``, you will be able to change +the widget for each task as follows: + +.. configuration-block:: + + .. code-block:: html+jinja + + {% form_theme form _self %} + + {% block _tasks_entry_widget %} + + {{ form_widget(task.task) }} + 8000 {{ form_widget(task.dueDate) }} + + {% endblock %} + + .. code-block:: html+php + + + + widget($form->task) ?> + widget($form->dueDate) ?> + + +Not only can you override the rendered widget, but you can also change the +complete form row or the label as well. For the ``tasks`` field given above, +the block names would be the following: + +================ ======================= +Part of the Form Block Name +================ ======================= +``label`` ``_tasks_entry_label`` +``widget`` ``_tasks_entry_widget`` +``row`` ``_tasks_entry_row`` +================ ======================= + +Then, you only have to ensure to render the collection type's ``data-prototype`` +property with the proper prototype so that new entries will be rendered the +same way as existing ones: + +.. configuration-block:: + + .. code-block:: html+jinja + + {% form_theme form _self %} + + {% block _tasks_widget %} + {% set attr = attr|merge({ 'data-prototype': form_row(prototype) }) %} + + {% for child in form %} + {{ form_row(child) }} + {% endfor %} +
+ {% endblock %} + + .. code-block:: html+php + + + .. _`Owning Side and Inverse Side`: http://docs.doctrine-project.org/en/latest/reference/unitofwork-associations.html .. _`JSFiddle`: http://jsfiddle.net/847Kf/4/ From 32b10608edd30533b61ad8c47bbf09b24e9f161d Mon Sep 17 00:00:00 2001 From: Ana Cicconi Date: Sat, 23 May 2015 13:49:57 +0200 Subject: [PATCH 0087/2667] Bootstrap form theme and checkboxes --- cookbook/form/form_customization.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cookbook/form/form_customization.rst b/cookbook/form/form_customization.rst index 7d2c513c8ea..87aa62da84a 100644 --- a/cookbook/form/form_customization.rst +++ b/cookbook/form/form_customization.rst @@ -114,6 +114,13 @@ fragment needed to render every part of a form: but the CSS classes applied are the ones used to display the forms horizontally (i.e. the label and the widget in the same row). +.. caution:: + + If you use the Bootstrap form theme and want to render the parts of + a checkbox field individually, do not use the Twig function {{ form_label() }}. + Otherwise, it will render nothing. + + In the next section you will learn how to customize a theme by overriding some or all of its fragments. From b80f46bcf48b55848ad5cb8868cc454ff29e6f62 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 23 May 2015 13:55:33 +0200 Subject: [PATCH 0088/2667] [WIP] [Translation] Add note about how to override translation in child Bundle #5193 --- cookbook/bundles/override.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cookbook/bundles/override.rst b/cookbook/bundles/override.rst index 70ba6786dde..c28a79f864b 100644 --- a/cookbook/bundles/override.rst +++ b/cookbook/bundles/override.rst @@ -191,7 +191,10 @@ can override the translations from any translation file, as long as it is in The last translation file always wins. That means that you need to make sure that the bundle containing *your* translations is loaded after any bundle whose translations you're overriding. This is done in ``AppKernel``. - + Translation files are not aware of the inheritance tree and therefore + unaware of their parent. If you want to override translations from the + parent bundle be sure the loading order in ``AppKernel`` is parent > child. + The file that always wins is the one that is placed in ``app/Resources/translations``, as those files are always loaded last. From 286f00cac7a3a40bed387b6952f733dc0401a0e3 Mon Sep 17 00:00:00 2001 From: Kevin Weber Date: Sat, 23 May 2015 10:00:54 -0400 Subject: [PATCH 0089/2667] Added security event descriptions to security component authentication page. --- components/security/authentication.rst | 48 ++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/components/security/authentication.rst b/components/security/authentication.rst index 00425a5d8f3..f720878a737 100644 --- a/components/security/authentication.rst +++ b/components/security/authentication.rst @@ -267,5 +267,53 @@ in) is correct, you can use:: $user->getSalt() ); +Events +------ + +The security component provides 4 related events: + +=============================== ================================================ ========================================================================= +Name Event Constant Argument Passed to the Listener +=============================== ================================================ ========================================================================= +security.authentication.success ``AuthenticationEvents::AUTHENTICATION_SUCCESS`` :class:`Symfony\Component\Security\Core\Event\AuthenticationEvent` +security.authentication.failure ``AuthenticationEvents::AUTHENTICATION_FAILURE`` :class:`Symfony\Component\Security\Core\Event\AuthenticationFailureEvent` +security.interactive_login ``SecurityEvents::INTERACTIVE_LOGIN`` :class:`Symfony\Component\Security\Http\Event\InteractiveLoginEvent` +security.switch_user ``SecurityEvents::SWITCH_USER`` :class:`Symfony\Component\Security\Http\Event\SwitchUserEvent` +=============================== ================================================ ========================================================================= + +Authentication Events +~~~~~~~~~~~~~~~~~~~~~ + +When a provider authenticates the user, a ``security.authentication.success`` +event is dispatched. Likewise, when no providers authenticate the user, +a ``security.authentication.failure`` event is dispatched. You +could listen on the ``security.authentication.failure`` event, for example, +in order to log failed login attempts. + +It is important to remember that one authentication event is always triggered +when a request points to a secured area. + +Security Events +~~~~~~~~~~~~~~~ + +The ``security.interactive_login`` event is triggered after a user has actively +logged into your website. It is important to distinguish this action from +non-interactive authentication methods, such as: + +* authentication based on a "remember me" cookie. +* authentication based on your session. +* authentication using a HTTP basic or HTTP digest header. + +You could listen on the ``security.interactive_login`` event, for example, in +order to give your user a welcome flash message every time they log in. + +The ``security.switch_user`` event is triggered every time you activate +the ``switch_user`` firewall listener. + +.. seealso:: + + For more information on switching users, see + :doc:`/cookbook/security/impersonating_user`. + .. _`CVE-2013-5750`: http://symfony.com/blog/cve-2013-5750-security-issue-in-fosuserbundle-login-form .. _`BasePasswordEncoder::checkPasswordLength`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Core/Encoder/BasePasswordEncoder.php From 13e335173eb546260cdf7ddf8bf43cba2f98569a Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 23 May 2015 16:03:28 +0200 Subject: [PATCH 0090/2667] Update override.rst Minor editing. --- cookbook/bundles/override.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cookbook/bundles/override.rst b/cookbook/bundles/override.rst index c28a79f864b..99bbec011fd 100644 --- a/cookbook/bundles/override.rst +++ b/cookbook/bundles/override.rst @@ -191,9 +191,10 @@ can override the translations from any translation file, as long as it is in The last translation file always wins. That means that you need to make sure that the bundle containing *your* translations is loaded after any bundle whose translations you're overriding. This is done in ``AppKernel``. - Translation files are not aware of the inheritance tree and therefore - unaware of their parent. If you want to override translations from the - parent bundle be sure the loading order in ``AppKernel`` is parent > child. + Translation files are not aware of the inheritance tree and therefore + unaware of their parent. If you want to override translations from the + parent bundle, be sure that the parent bundle is loaded before the child + bundle in the ``AppKernel`` class. The file that always wins is the one that is placed in ``app/Resources/translations``, as those files are always loaded last. From 9b2e9d867de8fb3eb500e31f901232fc434d47c5 Mon Sep 17 00:00:00 2001 From: Kevin Weber Date: Sat, 23 May 2015 10:15:58 -0400 Subject: [PATCH 0091/2667] Better section titles for security events on authentication page. --- components/security/authentication.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/security/authentication.rst b/components/security/authentication.rst index f720878a737..43f8c878faa 100644 --- a/components/security/authentication.rst +++ b/components/security/authentication.rst @@ -267,10 +267,10 @@ in) is correct, you can use:: $user->getSalt() ); -Events ------- +Authentication Events +--------------------- -The security component provides 4 related events: +The security component provides 4 related authentication events: =============================== ================================================ ========================================================================= Name Event Constant Argument Passed to the Listener @@ -281,8 +281,8 @@ security.interactive_login ``SecurityEvents::INTERACTIVE_LOGIN`` security.switch_user ``SecurityEvents::SWITCH_USER`` :class:`Symfony\Component\Security\Http\Event\SwitchUserEvent` =============================== ================================================ ========================================================================= -Authentication Events -~~~~~~~~~~~~~~~~~~~~~ +Authentication Success and Failure Events +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When a provider authenticates the user, a ``security.authentication.success`` event is dispatched. Likewise, when no providers authenticate the user, From f91e424c5cb896c17ff1c9e3bb3035e27e1d89bb Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 23 May 2015 16:37:07 +0200 Subject: [PATCH 0092/2667] Update override.rst Minor change. --- cookbook/bundles/override.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/bundles/override.rst b/cookbook/bundles/override.rst index 99bbec011fd..854a28c02db 100644 --- a/cookbook/bundles/override.rst +++ b/cookbook/bundles/override.rst @@ -195,7 +195,7 @@ can override the translations from any translation file, as long as it is in unaware of their parent. If you want to override translations from the parent bundle, be sure that the parent bundle is loaded before the child bundle in the ``AppKernel`` class. - + The file that always wins is the one that is placed in ``app/Resources/translations``, as those files are always loaded last. From 32d8203a03f8f72f61b0c218910c06f153fb1ea3 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 23 May 2015 16:45:24 +0200 Subject: [PATCH 0093/2667] Update override.rst Tackling the new line... --- cookbook/bundles/override.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/cookbook/bundles/override.rst b/cookbook/bundles/override.rst index 854a28c02db..b67ae24c656 100644 --- a/cookbook/bundles/override.rst +++ b/cookbook/bundles/override.rst @@ -198,5 +198,4 @@ can override the translations from any translation file, as long as it is in The file that always wins is the one that is placed in ``app/Resources/translations``, as those files are always loaded last. - .. _`the Doctrine documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html#overrides From 5bdfd71b4789b5e69f6d320349cb5038b0d43aa3 Mon Sep 17 00:00:00 2001 From: Kevin Weber Date: Sat, 23 May 2015 13:00:29 -0400 Subject: [PATCH 0094/2667] Better formatting for authentication tip. --- components/security/authentication.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/security/authentication.rst b/components/security/authentication.rst index 43f8c878faa..9f2a2d8b084 100644 --- a/components/security/authentication.rst +++ b/components/security/authentication.rst @@ -290,8 +290,10 @@ a ``security.authentication.failure`` event is dispatched. You could listen on the ``security.authentication.failure`` event, for example, in order to log failed login attempts. -It is important to remember that one authentication event is always triggered -when a request points to a secured area. +.. tip:: + + One of the authentication events is always triggered when a request points + to a secured area. Security Events ~~~~~~~~~~~~~~~ From f15bea451910fbba564ace171ba997278941dadc Mon Sep 17 00:00:00 2001 From: Kevin Weber Date: Sat, 23 May 2015 13:01:29 -0400 Subject: [PATCH 0095/2667] Added new cookbook page to security index. --- cookbook/security/index.rst | 1 + cookbook/security/throttle_failed_login.rst | 117 ++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 cookbook/security/throttle_failed_login.rst diff --git a/cookbook/security/index.rst b/cookbook/security/index.rst index 5bf643c10e8..04f05dc9efb 100644 --- a/cookbook/security/index.rst +++ b/cookbook/security/index.rst @@ -22,3 +22,4 @@ Security csrf_in_login_form access_control multiple_user_providers + throttle_failed_login diff --git a/cookbook/security/throttle_failed_login.rst b/cookbook/security/throttle_failed_login.rst new file mode 100644 index 00000000000..e96762b70d6 --- /dev/null +++ b/cookbook/security/throttle_failed_login.rst @@ -0,0 +1,117 @@ +.. index:: + single: Security; Throttle Failed Login + +How to Throttle Failed Login Attempts +===================================== + +Sometimes, it's useful to throttle login attempts when you encounter multiple +recent failed logins. This can be useful in combatting brute-force +password-guessing login attacks. This can be done by implementing a +``security.authentication.failure`` listener to log failed login attempts, and +a security provider decorator to deny login attempts from the same IP address. + +Authentication Failure Listener +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The purpose of this authentication failure listener is to persist the ip address +and any other data of the client. The example here uses a doctrine table to +persist the data, but you could use another method (Redis, for example). + +.. code-block:: php + + namespace AppBundle\Security\Authentication\Listener; + + use Doctrine\ORM\EntityRepositoryInterface; + use Symfony\Component\HttpFoundation\RequestStack; + use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent; + + class LogFailedLoginAttempt + { + /** + * The doctrine repository to which the failed login attempt will be + * persisted. + * + * @var EntityRepositoryInterface + */ + private $repository; + + /** + * The current request contains the ip address and other $_SERVER globals + * to persist. + * + * @var RequestStack + */ + private $requestStack; + + public function __construct(RequestStack $requestStack, EntityRepositoryInterface $repository) + { + $this->repository = $repository; + $this->requestStack = $requestStack; + } + + public function onAuthenticationFailureEvent(AuthenticationFailureEvent $event) + { + $this->repository->logAuthenicationFailure( + $event->getToken(), + $this->requestStack->getCurrentRequest() + ); + } + } + +Now you are logging all the relevant details about every failed login attempt. + +Security Provider Decorator +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This security provider will decorate another security provider and will reject +any authentication attempts it needs to throttle. + +.. code-block:: php + + namespace AppBundle\Security\Authentication\Provider; + + use Symfony\Component\Security\Core\Exception\AuthenticationException; + use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; + use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + + class LoginThrottleProvider implements AuthenticationProviderInterface + { + /** + * @var AuthenticationProviderInterface + */ + private $decoratedProvider; + + /** + * The throttle service decides whether to throttle a certain + * ip address or not. + */ + private $throttleService; + + public function __construct(AuthenticationProviderInterface $provider, $throttleService) + { + $this->decoratedProvider = $provider; + $this->throttleService = $throttleService; + } + + public function authenticate(TokenInterface $token) + { + if ($this->throttleService->isThrottled($token)) { + throw new AuthenticationException( + 'Too many failed authentication attempts.' + ); + } + + return $this->decoratedProvider->authenticate($token); + } + + public function supports(TokenInterface $token) + { + return $this->decoratedProvider->supports($token); + } + } + +The implementation of the throttle service is outside the scope of this +documentation and will depend on your application's needs. It is common +for an application to require additional means of authentication when +multiple failed logins are detected, such as the addition of a CAPTCHA +to the login page, or requiring two-factor authentication. From 19324e838ff43d471b387a0cf84948d3b0acb01e Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 23 May 2015 23:34:02 +0200 Subject: [PATCH 0096/2667] Update data_transformers.rst #2471 --- cookbook/form/data_transformers.rst | 126 ++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 36 deletions(-) diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index 8bbcea505c1..1dafc3b1ffb 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -111,29 +111,96 @@ for converting to and from the issue number and the ``Issue`` object:: Using the Transformer --------------------- -Now that you have the transformer built, you just need to add it to your -issue field in some form. +As seen above our transformer requires an instance of an object manager. While for most +use-cases using the default manager is fine we will let you pick the manager by it's name. +In order to achieve this we will add a factory: + + // src/Acme/TaskBundle/Form/DataTransformer/IssueToNumberTransformerFactory.php + namespace Acme\TaskBundle\Form\DataTransformer; + + use Symfony\Bridge\Doctrine\ManagerRegistry; + + class IssueToNumberTransformerFactory + { + /** @var ManagerRegistry */ + private $registry; + + public function __construct(ManagerRegistry $registry) + { + $this->registry = $registry; + } + + public function create($om) + { + return new IssueToNumberTransformer($this->registry->getManager($om)); + } + } + +.. configuration-block:: + + .. code-block:: yaml + + services: + acme_demo.factory.issue_transformer: + class: Acme\TaskBundle\Form\DataTransformer\IssueToNumberTransformerFactory + arguments: ["@doctrine"] + + acme_demo.type.task: + class: Acme\TaskBundle\Form\TaskType + arguments: ["@acme_demo.factory.issue_transformer"] + + .. code-block:: xml + + + + + + + + + + .. code-block:: php + + $container + ->setDefinition('acme_demo.factory.issue_transformer', array( + new Reference('doctrine'), + )) + ; + + $container + ->setDefinition('acme_demo.type.task', array( + new Reference('acme_demo.factory.issue_transformer'), + )) + ; + +Now that you have capability to build the transformer with the desired object manager, you +just need to create it from your issue field in some form. You can also use transformers without creating a new custom form type by calling ``addModelTransformer`` (or ``addViewTransformer`` - see `Model and View Transformers`_) on any field builder:: + // src/Acme/TaskBundle/Form/TaskType.php + namespace Acme\TaskBundle\Form; + + use Acme\TaskBundle\Form\DataTransformer\IssueToNumberTransformerFactory; use Symfony\Component\Form\FormBuilderInterface; - use Acme\TaskBundle\Form\DataTransformer\IssueToNumberTransformer; + use Symfony\Component\OptionsResolver\OptionsResolverInterface; class TaskType extends AbstractType { - public function buildForm(FormBuilderInterface $builder, array $options) + /** @var IssueToNumberTransformerFactory */ + private $factory; + + public function __construct(IssueToNumberTransformerFactory $factory) { - // ... + $this->factory = $factory; + } - // the "em" is an option that you pass when creating your form. Check out - // the 3rd argument to createForm in the next code block to see how this - // is passed to the form (also see setDefaultOptions). - $entityManager = $options['em']; - $transformer = new IssueToNumberTransformer($entityManager); + public function buildForm(FormBuilderInterface $builder, array $options) + { + $transformer = $this->factory->create($options['om']); - // add a normal text field, but add your transformer to it $builder->add( $builder->create('issue', 'text') ->addModelTransformer($transformer) @@ -146,25 +213,18 @@ by calling ``addModelTransformer`` (or ``addViewTransformer`` - see ->setDefaults(array( 'data_class' => 'Acme\TaskBundle\Entity\Task', )) - ->setRequired(array( - 'em', - )) - ->setAllowedTypes(array( - 'em' => 'Doctrine\Common\Persistence\ObjectManager', - )); - - // ... + ->setRequired(array('om')) + ; } - - // ... } This example requires that you pass in the entity manager as an option when creating your form. Later, you'll learn how you could create a custom ``issue`` field type to avoid needing to do this in your controller:: - $taskForm = $this->createForm(new TaskType(), $task, array( - 'em' => $this->getDoctrine()->getManager(), + $taskType = $this->get('acme_demo.type.task'); + $taskForm = $this->createForm($taskType, $task, array( + 'om' => 'default', )); Cool, you're done! Your user will be able to enter an issue number into the @@ -257,30 +317,23 @@ First, create the custom field type class:: // src/Acme/TaskBundle/Form/Type/IssueSelectorType.php namespace Acme\TaskBundle\Form\Type; + use Acme\TaskBundle\Form\DataTransformer\IssueToNumberTransformerFactory; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; - use Acme\TaskBundle\Form\DataTransformer\IssueToNumberTransformer; - use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\OptionsResolver\OptionsResolverInterface; class IssueSelectorType extends AbstractType { - /** - * @var ObjectManager - */ - private $om; - - /** - * @param ObjectManager $om - */ - public function __construct(ObjectManager $om) + private $factory; + + public function __construct(IssueToNumberTransformerFactory $factory) { - $this->om = $om; + $this->factory = $factory; } public function buildForm(FormBuilderInterface $builder, array $options) { - $transformer = new IssueToNumberTransformer($this->om); + $transformer = $this->factory->create($options['om']); $builder->addModelTransformer($transformer); } @@ -288,6 +341,7 @@ First, create the custom field type class:: { $resolver->setDefaults(array( 'invalid_message' => 'The selected issue does not exist', + 'om' => 'default' )); } From 7929ed58f3540a5306988783c207e40e32ce5ed9 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 23 May 2015 23:44:58 +0200 Subject: [PATCH 0097/2667] Update data_transformers.rst Minor change. --- cookbook/form/data_transformers.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index 1dafc3b1ffb..9ffa5c3282c 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -122,7 +122,9 @@ In order to achieve this we will add a factory: class IssueToNumberTransformerFactory { - /** @var ManagerRegistry */ + /** + * @var ManagerRegistry + */ private $registry; public function __construct(ManagerRegistry $registry) @@ -189,7 +191,9 @@ by calling ``addModelTransformer`` (or ``addViewTransformer`` - see class TaskType extends AbstractType { - /** @var IssueToNumberTransformerFactory */ + /** + * @var IssueToNumberTransformerFactory + */ private $factory; public function __construct(IssueToNumberTransformerFactory $factory) From 4b09b3fed4328126ca31be7bc1572173bd1df0eb Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 23 May 2015 23:47:03 +0200 Subject: [PATCH 0098/2667] Update data_transformers.rst Minor change. --- cookbook/form/data_transformers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index 9ffa5c3282c..b41d359dd64 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -113,7 +113,7 @@ Using the Transformer As seen above our transformer requires an instance of an object manager. While for most use-cases using the default manager is fine we will let you pick the manager by it's name. -In order to achieve this we will add a factory: +In order to achieve this we will add a factory:: // src/Acme/TaskBundle/Form/DataTransformer/IssueToNumberTransformerFactory.php namespace Acme\TaskBundle\Form\DataTransformer; From 939cfc585c968e86af3139d660772e347194e477 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 25 May 2015 14:07:47 +0200 Subject: [PATCH 0099/2667] Update data_transformers.rst Couple of changes. --- cookbook/form/data_transformers.rst | 124 ++++++++++++++++++---------- 1 file changed, 81 insertions(+), 43 deletions(-) diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index b41d359dd64..c2ce3ae0486 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -28,13 +28,13 @@ Creating the Transformer First, create an ``IssueToNumberTransformer`` class - this class will be responsible for converting to and from the issue number and the ``Issue`` object:: - // src/Acme/TaskBundle/Form/DataTransformer/IssueToNumberTransformer.php - namespace Acme\TaskBundle\Form\DataTransformer; + // src/AppBundle/Form/DataTransformer/IssueToNumberTransformer.php + namespace AppBundle\Form\DataTransformer; + use AppBundle\Entity\Issue; + use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\Exception\TransformationFailedException; - use Doctrine\Common\Persistence\ObjectManager; - use Acme\TaskBundle\Entity\Issue; class IssueToNumberTransformer implements DataTransformerInterface { @@ -60,7 +60,7 @@ for converting to and from the issue number and the ``Issue`` object:: public function transform($issue) { if (null === $issue) { - return ""; + return ''; } return $issue->getNumber(); @@ -70,9 +70,7 @@ for converting to and from the issue number and the ``Issue`` object:: * Transforms a string (number) to an object (issue). * * @param string $number - * * @return Issue|null - * * @throws TransformationFailedException if object (issue) is not found. */ public function reverseTransform($number) @@ -82,7 +80,7 @@ for converting to and from the issue number and the ``Issue`` object:: } $issue = $this->om - ->getRepository('AcmeTaskBundle:Issue') + ->getRepository('AppBundle:Issue') ->findOneBy(array('number' => $number)) ; @@ -112,13 +110,13 @@ Using the Transformer --------------------- As seen above our transformer requires an instance of an object manager. While for most -use-cases using the default manager is fine we will let you pick the manager by it's name. -In order to achieve this we will add a factory:: +use-cases it is sufficient to use the default entity manager, you will sometimes need +to explicitly choose the one to use. To achieve this, you can use a factory:: - // src/Acme/TaskBundle/Form/DataTransformer/IssueToNumberTransformerFactory.php - namespace Acme\TaskBundle\Form\DataTransformer; + // src/AppBundle/Form/DataTransformer/IssueToNumberTransformerFactory.php + namespace AppBundle\Form\DataTransformer; - use Symfony\Bridge\Doctrine\ManagerRegistry; + use Doctrine\Common\Persistence\ManagerRegistry; class IssueToNumberTransformerFactory { @@ -143,49 +141,65 @@ In order to achieve this we will add a factory:: .. code-block:: yaml services: - acme_demo.factory.issue_transformer: - class: Acme\TaskBundle\Form\DataTransformer\IssueToNumberTransformerFactory + app.issue_transformer_factory: + class: AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory arguments: ["@doctrine"] + public: false - acme_demo.type.task: - class: Acme\TaskBundle\Form\TaskType - arguments: ["@acme_demo.factory.issue_transformer"] + app.type.task: + class: AppBundle\Form\TaskType + arguments: ["@app.issue_transformer_factory"] + tag: + - { name: form.type, alias: app_task } .. code-block:: xml - + - - + + + .. code-block:: php + use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\DependencyInjection\Reference; + // ... + $container - ->setDefinition('acme_demo.factory.issue_transformer', array( + ->setDefinition('app.issue_transformer_factory', new Definition( + 'AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory' + ), array( new Reference('doctrine'), )) + ->setPublic(false) ; $container - ->setDefinition('acme_demo.type.task', array( - new Reference('acme_demo.factory.issue_transformer'), + ->setDefinition('app.type.task', new Definition( + 'AppBundle\Form\TaskType' + ), array( + new Reference('app.issue_transformer_factory'), )) + ->addTag('form.type', array('alias' => 'app_task')) ; -Now that you have capability to build the transformer with the desired object manager, you +Now that you have the capability to build the transformer with the desired object manager, you just need to create it from your issue field in some form. You can also use transformers without creating a new custom form type by calling ``addModelTransformer`` (or ``addViewTransformer`` - see `Model and View Transformers`_) on any field builder:: - // src/Acme/TaskBundle/Form/TaskType.php - namespace Acme\TaskBundle\Form; + // src/AppBundle/Form/TaskType.php + namespace AppBundle\Form; - use Acme\TaskBundle\Form\DataTransformer\IssueToNumberTransformerFactory; + use AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; @@ -215,7 +229,7 @@ by calling ``addModelTransformer`` (or ``addViewTransformer`` - see { $resolver ->setDefaults(array( - 'data_class' => 'Acme\TaskBundle\Entity\Task', + 'data_class' => 'AppBundle\Entity\Task', )) ->setRequired(array('om')) ; @@ -226,8 +240,7 @@ This example requires that you pass in the entity manager as an option when creating your form. Later, you'll learn how you could create a custom ``issue`` field type to avoid needing to do this in your controller:: - $taskType = $this->get('acme_demo.type.task'); - $taskForm = $this->createForm($taskType, $task, array( + $taskForm = $this->createForm('app_task', $task, array( 'om' => 'default', )); @@ -318,10 +331,10 @@ a form that uses the transformer. Because of these, you may choose to :doc:`create a custom field type `. First, create the custom field type class:: - // src/Acme/TaskBundle/Form/Type/IssueSelectorType.php - namespace Acme\TaskBundle\Form\Type; + // src/AppBundle/Form/IssueSelectorType.php + namespace AppBundle\Form; - use Acme\TaskBundle\Form\DataTransformer\IssueToNumberTransformerFactory; + use AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; @@ -368,24 +381,49 @@ it's recognized as a custom field type: .. code-block:: yaml services: - acme_demo.type.issue_selector: - class: Acme\TaskBundle\Form\Type\IssueSelectorType - arguments: ["@doctrine.orm.entity_manager"] + app.issue_transformer_factory: + class: AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory + arguments: ["@doctrine"] + public: false + app.type.issue_selector: + class: AppBundle\Form\IssueSelectorType + arguments: ["@app.issue_transformer_factory"] tags: - { name: form.type, alias: issue_selector } .. code-block:: xml - - + + + + + + .. code-block:: php + use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\DependencyInjection\Reference; + // ... + + $container + ->setDefinition('app.issue_transformer_factory', new Definition( + 'AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory' + ), array( + new Reference('doctrine'), + )) + ->setPublic(false) + ; + $container - ->setDefinition('acme_demo.type.issue_selector', array( - new Reference('doctrine.orm.entity_manager'), + ->setDefinition('app.type.issue_selector', new Definition( + 'AppBundle\Form\IssueSelectorType' + ), array( + new Reference('app.issue_transformer_factory'), )) ->addTag('form.type', array( 'alias' => 'issue_selector', @@ -395,8 +433,8 @@ it's recognized as a custom field type: Now, whenever you need to use your special ``issue_selector`` field type, it's quite easy:: - // src/Acme/TaskBundle/Form/Type/TaskType.php - namespace Acme\TaskBundle\Form\Type; + // src/AppBundle/Form/TaskType.php + namespace AppBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; From 0f8f9fdf0d6203dac18555fd1805fe2eff00fa40 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 16:03:14 +0200 Subject: [PATCH 0100/2667] Documented the useAttributeAsKey() method --- components/config/definition.rst | 70 +++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/components/config/definition.rst b/components/config/definition.rst index b79ad3853a2..c6a26f35469 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -200,25 +200,58 @@ Array Node Options Before defining the children of an array node, you can provide options like: -``useAttributeAsKey()`` - Provide the name of a child node, whose value should be used as the key in the resulting array. -``requiresAtLeastOneElement()`` - There should be at least one element in the array (works only when ``isRequired()`` is also - called). ``addDefaultsIfNotSet()`` - If any child nodes have default values, use them if explicit values haven't been provided. + If any child nodes have default values, use them if explicit values haven't + been provided. +``requiresAtLeastOneElement()`` + There should be at least one element in the array (works only when + ``isRequired()`` is also called). +``useAttributeAsKey()`` + Provide the name of a child node, whose value should be used as the key in + the resulting array. This method also defines the way config array keys are + treated, as explained in the following example. + +When the ``useAttributeAsKey()`` method is not used, the names of the array +elements (i.e. the array keys) are ignored when parsing the configuration. +Consider this example:: + + $rootNode + ->children() + ->arrayNode('parameters') + ->prototype('array') + ->children() + ->scalarNode('parameter1')->end() + ->scalarNode('parameter2')->end() + ->end() + ->end() + ->end() + ->end() + ; + +In YAML, the configuration might look like this: + +.. code-block:: yaml + + database: + parameters: [ 'value1', 'value2' ] -An example of this:: +In XML, the configuration might look like this: + +.. code-block:: xml + + ... + +However, if the ``useAttributeAsKey()`` method is set, the parsed configuration +will be completely different:: $rootNode ->children() ->arrayNode('parameters') - ->isRequired() - ->requiresAtLeastOneElement() - ->useAttributeAsKey('name') + ->useAttributeAsKey('value') ->prototype('array') ->children() - ->scalarNode('value')->isRequired()->end() + ->scalarNode('parameter1')->end() + ->scalarNode('parameter2')->end() ->end() ->end() ->end() @@ -231,12 +264,19 @@ In YAML, the configuration might look like this: database: parameters: - param1: { value: param1val } + parameter1: { value: 'value1' } + parameter2: { value: 'value2' } + +In XML, the configuration might look like this: + +.. code-block:: xml + + ... -In XML, each ``parameters`` node would have a ``name`` attribute (along with +In XML, each ``parameters`` node has a ``value`` attribute (along with ``value``), which would be removed and used as the key for that element in -the final array. The ``useAttributeAsKey`` is useful for normalizing how -arrays are specified between different formats like XML and YAML. +the final array. The ``useAttributeAsKey()`` method is useful for normalizing +how arrays are specified between different formats like XML and YAML. Default and required Values --------------------------- From e77c3b28e8c78863f97e202f2d4709a50dc347b1 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 17:04:28 +0200 Subject: [PATCH 0101/2667] Rewritten the explanation about the useAttributeAsKey() method --- components/config/definition.rst | 143 +++++++++++++++++++++++-------- 1 file changed, 109 insertions(+), 34 deletions(-) diff --git a/components/config/definition.rst b/components/config/definition.rst index c6a26f35469..f77855870f9 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -211,72 +211,147 @@ Before defining the children of an array node, you can provide options like: the resulting array. This method also defines the way config array keys are treated, as explained in the following example. -When the ``useAttributeAsKey()`` method is not used, the names of the array -elements (i.e. the array keys) are ignored when parsing the configuration. -Consider this example:: +A basic prototyped array configuration can be defined as follows:: - $rootNode + $node + ->fixXmlConfig('driver') ->children() - ->arrayNode('parameters') - ->prototype('array') - ->children() - ->scalarNode('parameter1')->end() - ->scalarNode('parameter2')->end() - ->end() - ->end() + ->arrayNode('drivers') + ->prototype('scalar')->end() ->end() ->end() ; -In YAML, the configuration might look like this: +When using the following YAML configuration: .. code-block:: yaml - database: - parameters: [ 'value1', 'value2' ] + drivers: ['mysql', 'sqlite'] -In XML, the configuration might look like this: +Or the following XML configuration: .. code-block:: xml - ... + msyql + sqlite -However, if the ``useAttributeAsKey()`` method is set, the parsed configuration -will be completely different:: +The processed configuration is:: - $rootNode + Array( + [0] => 'mysql' + [1] => 'sqlite' + ) + +A more complex example would be to define a prototyped array with children: + + $node + ->fixXmlConfig('connection') ->children() - ->arrayNode('parameters') - ->useAttributeAsKey('value') + ->arrayNode('connections') ->prototype('array') ->children() - ->scalarNode('parameter1')->end() - ->scalarNode('parameter2')->end() + ->scalarNode('table')->end() + ->scalarNode('user')->end() + ->scalarNode('password')->end() ->end() ->end() ->end() ->end() ; -In YAML, the configuration might look like this: +When using the following YAML configuration: .. code-block:: yaml - database: - parameters: - parameter1: { value: 'value1' } - parameter2: { value: 'value2' } + connections: + - { table: symfony, user: root, password: ~ } + - { table: foo, user: root, password: pa$$ } -In XML, the configuration might look like this: +Or the following XML configuration: .. code-block:: xml - ... + + + +The processed configuration is:: + + Array( + [0] => Array( + [table] => 'symfony' + [user] => 'root' + [password] => null + ) + [1] => Array( + [table] => 'foo' + [user] => 'root' + [password] => 'pa$$' + ) + ) + +The previous output matches the expected result. However, given the configuration +tree, when using the following YAML configuration: + +.. code-block:: yaml + + connections: + sf_connection: + table: symfony + user: root + password: ~ + default: + table: foo + user: root + password: pa$$ + +The output configuration will be exactly the same as before. In other words, the +``sf_connection`` and ``default`` configuration keys are lost. The reason is that +the Symfony Config component treats arrays as lists by default. + +In order to maintain the array keys use the ``useAttributeAsKey()`` method:: + + $node + ->fixXmlConfig('connection') + ->children() + ->arrayNode('connections') + ->prototype('array') + ->useAttributeAsKey('name') + ->children() + ->scalarNode('table')->end() + ->scalarNode('user')->end() + ->scalarNode('password')->end() + ->end() + ->end() + ->end() + ->end() + ; + +The argument of this method (``name`` in the example above) defines the name of +the attribute added to each XML node to differentiate them. Now you can use the +same YAML configuration showed before or the following XML configuration: + +.. code-block:: xml -In XML, each ``parameters`` node has a ``value`` attribute (along with -``value``), which would be removed and used as the key for that element in -the final array. The ``useAttributeAsKey()`` method is useful for normalizing -how arrays are specified between different formats like XML and YAML. + + + +In both cases, the processed configuration maintains the ``sf_connection`` and +``default`` keys:: + + Array( + [sf_connection] => Array( + [table] => 'symfony' + [user] => 'root' + [password] => null + ) + [default] => Array( + [table] => 'foo' + [user] => 'root' + [password] => 'pa$$' + ) + ) Default and required Values --------------------------- From 9fe902060cf1a957998bba52c551f23c36fe6ef1 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 26 May 2015 12:48:00 +0200 Subject: [PATCH 0102/2667] Fixed a minor syntax issue --- components/config/definition.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/config/definition.rst b/components/config/definition.rst index f77855870f9..abce20b6a2c 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -242,7 +242,7 @@ The processed configuration is:: [1] => 'sqlite' ) -A more complex example would be to define a prototyped array with children: +A more complex example would be to define a prototyped array with children:: $node ->fixXmlConfig('connection') From 402f5d49e75e2004e1507c3e953ebd0c32223732 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 17 Apr 2015 09:43:26 +0200 Subject: [PATCH 0103/2667] Added a new articule about using/installing inestable Symfony versions --- cookbook/index.rst | 1 + cookbook/install/index.rst | 7 ++ cookbook/install/inestable_versions | 95 +++++++++++++++++++ cookbook/install/upgrading.rst | 141 ++++++++++++++++++++++++++++ cookbook/map.rst.inc | 8 ++ 5 files changed, 252 insertions(+) create mode 100644 cookbook/install/index.rst create mode 100644 cookbook/install/inestable_versions create mode 100644 cookbook/install/upgrading.rst diff --git a/cookbook/index.rst b/cookbook/index.rst index 3587d3e4efd..98828a56287 100644 --- a/cookbook/index.rst +++ b/cookbook/index.rst @@ -18,6 +18,7 @@ The Cookbook event_dispatcher/index form/index frontend/index + install/index logging/index profiler/index request/index diff --git a/cookbook/install/index.rst b/cookbook/install/index.rst new file mode 100644 index 00000000000..dc0f2c4212f --- /dev/null +++ b/cookbook/install/index.rst @@ -0,0 +1,7 @@ +Install and Upgrade +=================== + +.. toctree:: + :maxdepth: 2 + + upgrading diff --git a/cookbook/install/inestable_versions b/cookbook/install/inestable_versions new file mode 100644 index 00000000000..e9957c1b11f --- /dev/null +++ b/cookbook/install/inestable_versions @@ -0,0 +1,95 @@ +How to Install and Use an Inestable Symfony Version +=================================================== + +Symfony releases two new minor versions (2.5, 2.6, 2.7, etc.) per year, one in +May and one in November (:doc:`see releases detail `). +Testing the new Symfony versions in your projects as soon as possible is important +to ensure that they will keep working as expected. + +In this article you'll learn how to install and use new Symfony versions before +they are released as stable versions. + +Creating a New Project Based on an Inestable Symfony Version +------------------------------------------------------------ + +Suppose that Symfony 2.7 version hasn't been released yet and you want to create +a new project to test its features. First, :doc:`install Composer ` +package manager. Then, open a command console, enter your projects directory and +execute the following command: + +.. code-block:: bash + + $ composer create-project symfony/framework-standard-edition my_project "2.7.*" --stability=dev + +Once the command finishes its execution, you'll have a new Symfony project created +in the ``my_project/`` directory and based on the most recent code found in the +``2.7`` branch. + +If you want to test a beta version, use ``beta`` as the value of the ``stability`` +option: + +.. code-block:: bash + + $ composer create-project symfony/framework-standard-edition my_project "2.7.*" --stability=beta + +Upgrading your Project to an Inestable Symfony Version +------------------------------------------------------ + +Instead of creating a new empty project, in this section you'll update an existing +Symfony application to an inestable framework version. Suppose again that Symfony +2.7 version hasn't been released yet and you want to test it in your project. + +First, open the ``composer.json`` file located in the root directory of your +project. Then, edit the value of the version defined for the ``symfony/symfony`` +dependency: + +.. code-block:: json + + { + "require": { + // ... + "symfony/symfony" : "2.7.*" + } + } + +Then, before updating your dependencies, make sure that the project configuration +allows to install inestable versions. If the ``composer.json`` file contains a +``minimum-stability`` option, change its value to ``dev``. If that option doesn't +exist, add it as follows: + +.. code-block:: json + + { + "require": { + // ... + "symfony/symfony" : "2.7.*" + }, + "minimum-stability": "dev" + } + +If you prefer to test a Symfony beta version, replace the ``dev`` value of the +``minimum-stability`` option by ``beta``. + +Then, open a command console, enter your project directory and execute the following command to update your project dependencies: + +.. code-block:: bash + + $ composer update + +.. tip:: + + If you use Git to manage the project's code, it's a good practice to create + a new branch to test the new Symfony version. This solution avoids introducing + any issue in your application and allows you to test the new version with + total confidence: + + .. code-block:: bash + + $ cd projects/my_project/ + $ git checkout -b testing_new_symfony + // update composer.json configuration + $ composer update + + // ... after testing the new Symfony version + $ git checkout master + $ git branch -D testing_new_symfony diff --git a/cookbook/install/upgrading.rst b/cookbook/install/upgrading.rst new file mode 100644 index 00000000000..88a5ecc22a6 --- /dev/null +++ b/cookbook/install/upgrading.rst @@ -0,0 +1,141 @@ +How to Upgrade Your Symfony Project +=================================== + +So a new Symfony release has come out and you want to upgrade, great! Fortunately, +because Symfony protects backwards-compatibility very closely, this *should* +be quite easy. + +There are two types of upgrades, and both are a little different: + +* :ref:`upgrading-patch-version` +* :ref:`upgrading-minor-version` + +.. _upgrading-patch-version: + +Upgrading a Patch Version (e.g. 2.6.0 to 2.6.1) +----------------------------------------------- + +If you're upgrading and only the patch version (the last number) is changing, +then it's *really* easy: + +.. code-block:: bash + + $ composer update symfony/symfony + +That's it! You should not encounter any backwards-compatibility breaks or +need to change anything else in your code. That's because when you started +your project, your ``composer.json`` included Symfony using a constraint +like ``2.6.*``, where only the *last* version number will change when you +update. + +You may also want to upgrade the rest of your libraries. If you've done a +good job with your `version constraints`_ in ``composer.json``, you can do +this safely by running: + +.. code-block:: bash + + $ composer update + +But beware. If you have some bad `version constraints`_ in your ``composer.json``, +(e.g. ``dev-master``), then this could upgrade some non-Symfony libraries +to new versions that contain backwards-compatibility breaking changes. + +.. _upgrading-minor-version: + +Upgrading a Minor Version (e.g. 2.5.3 to 2.6.1) +----------------------------------------------- + +If you're upgrading a minor version (where the middle number changes), then +you should also *not* encounter significant backwards compatibility changes. +For details, see our :doc:`/contributing/code/bc`. + +However, some backwards-compatibility breaks *are* possible, and you'll learn +in a second how to prepare for them. + +There are two steps to upgrading: + +:ref:`upgrade-minor-symfony-composer`; +:ref:`upgrade-minor-symfony-code` + +.. _`upgrade-minor-symfony-composer`: + +1) Update the Symfony Library via Composer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +First, you need to update Symfony by modifying your ``composer.json`` file +to use the new version: + +.. code-block:: json + + { + "...": "...", + + "require": { + "php": ">=5.3.3", + "symfony/symfony": "2.6.*", + "...": "... no changes to anything else..." + }, + "...": "...", + } + +Next, use Composer to download new versions of the libraries: + +.. code-block:: bash + + $ composer update symfony/symfony + +You may also want to upgrade the rest of your libraries. If you've done a +good job with your `version constraints`_ in ``composer.json``, you can do +this safely by running: + +.. code-block:: bash + + $ composer update + +But beware. If you have some bad `version constraints`_ in your ``composer.json``, +(e.g. ``dev-master``), then this could upgrade some non-Symfony libraries +to new versions that contain backwards-compatibility breaking changes. + +.. _`upgrade-minor-symfony-code`: + +2) Updating Your Code to Work with the new Version +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In theory, you should be done! However, you *may* need to make a few changes +to your code to get everything working. Additionally, some features you're +using might still work, but might now be deprecated. That's actually ok, +but if you know about these deprecations, you can start to fix them over +time. + +Every version of Symfony comes with an UPGRADE file that describes these +changes. Below are links to the file for each version, which you'll need +to read to see if you need any code changes. + +.. tip:: + + Don't see the version here that you're upgrading to? Just find the + UPGRADE-X.X.md file for the appropriate version on the `Symfony Repository`_. + +Upgrading to Symfony 2.6 +........................ + +First, of course, update your ``composer.json`` file with the ``2.6`` version +of Symfony as described above in :ref:`upgrade-minor-symfony-composer`. + +Next, check the `UPGRADE-2.6`_ document for details about any code changes +that you might need to make in your project. + +Upgrading to Symfony 2.5 +........................ + +First, of course, update your ``composer.json`` file with the ``2.5`` version +of Symfony as described above in :ref:`upgrade-minor-symfony-composer`. + +Next, check the `UPGRADE-2.5`_ document for details about any code changes +that you might need to make in your project. + +.. _`UPGRADE-2.5`: https://github.com/symfony/symfony/blob/2.5/UPGRADE-2.5.md +.. _`UPGRADE-2.6`: https://github.com/symfony/symfony/blob/2.6/UPGRADE-2.6.md +.. _`Symfony Repository`: https://github.com/symfony/symfony +.. _`Composer Package Versions`: https://getcomposer.org/doc/01-basic-usage.md#package-versions +.. _`version constraints`: https://getcomposer.org/doc/01-basic-usage.md#package-versions diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 2c4d8eb94f6..c45d0942167 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -112,6 +112,11 @@ * :doc:`/cookbook/frontend/bower` +* **Installing and Upgrading** + + * :doc:`/cookbook/upgrading` + * :doc:`/cookbook/inestable_version` + * :doc:`/cookbook/logging/index` * :doc:`/cookbook/logging/monolog` @@ -207,12 +212,15 @@ * (email) :doc:`/cookbook/email/testing` * (form) :doc:`/cookbook/form/unit_testing` +<<<<<<< HEAD * :doc:`/cookbook/upgrade/index` * :doc:`/cookbook/upgrade/patch_version` * :doc:`/cookbook/upgrade/minor_version` * :doc:`/cookbook/upgrade/major_version` +======= +>>>>>>> Added a new articule about using/installing inestable Symfony versions * :doc:`/cookbook/validation/index` * :doc:`/cookbook/validation/custom_constraint` From df22b75dcf9059427dfec84c3bbaa03a3131fb75 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 17 Apr 2015 09:59:26 +0200 Subject: [PATCH 0104/2667] Fixed typo: "inestable" -> "unstable" --- cookbook/install/inestable_versions | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cookbook/install/inestable_versions b/cookbook/install/inestable_versions index e9957c1b11f..b93403e4d19 100644 --- a/cookbook/install/inestable_versions +++ b/cookbook/install/inestable_versions @@ -1,5 +1,5 @@ -How to Install and Use an Inestable Symfony Version -=================================================== +How to Install and Use an Unstable Symfony Version +================================================== Symfony releases two new minor versions (2.5, 2.6, 2.7, etc.) per year, one in May and one in November (:doc:`see releases detail `). @@ -9,8 +9,8 @@ to ensure that they will keep working as expected. In this article you'll learn how to install and use new Symfony versions before they are released as stable versions. -Creating a New Project Based on an Inestable Symfony Version ------------------------------------------------------------- +Creating a New Project Based on an Unstable Symfony Version +----------------------------------------------------------- Suppose that Symfony 2.7 version hasn't been released yet and you want to create a new project to test its features. First, :doc:`install Composer ` @@ -32,11 +32,11 @@ option: $ composer create-project symfony/framework-standard-edition my_project "2.7.*" --stability=beta -Upgrading your Project to an Inestable Symfony Version ------------------------------------------------------- +Upgrading your Project to an Unstable Symfony Version +----------------------------------------------------- Instead of creating a new empty project, in this section you'll update an existing -Symfony application to an inestable framework version. Suppose again that Symfony +Symfony application to an unstable framework version. Suppose again that Symfony 2.7 version hasn't been released yet and you want to test it in your project. First, open the ``composer.json`` file located in the root directory of your @@ -53,7 +53,7 @@ dependency: } Then, before updating your dependencies, make sure that the project configuration -allows to install inestable versions. If the ``composer.json`` file contains a +allows to install unstable versions. If the ``composer.json`` file contains a ``minimum-stability`` option, change its value to ``dev``. If that option doesn't exist, add it as follows: From 27229330682deb36bbace6dfb4ea736e500c79ae Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 17 Apr 2015 10:01:36 +0200 Subject: [PATCH 0105/2667] Fixed articles links --- cookbook/install/index.rst | 1 + .../install/{inestable_versions => unstable_versions} | 0 cookbook/map.rst.inc | 9 +++------ 3 files changed, 4 insertions(+), 6 deletions(-) rename cookbook/install/{inestable_versions => unstable_versions} (100%) diff --git a/cookbook/install/index.rst b/cookbook/install/index.rst index dc0f2c4212f..9177815770e 100644 --- a/cookbook/install/index.rst +++ b/cookbook/install/index.rst @@ -5,3 +5,4 @@ Install and Upgrade :maxdepth: 2 upgrading + unstable_versions diff --git a/cookbook/install/inestable_versions b/cookbook/install/unstable_versions similarity index 100% rename from cookbook/install/inestable_versions rename to cookbook/install/unstable_versions diff --git a/cookbook/map.rst.in 8000 c b/cookbook/map.rst.inc index c45d0942167..797e6658e53 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -112,10 +112,10 @@ * :doc:`/cookbook/frontend/bower` -* **Installing and Upgrading** +* :doc:`/cookbook/install/index` - * :doc:`/cookbook/upgrading` - * :doc:`/cookbook/inestable_version` + * :doc:`/cookbook/install/upgrading` + * :doc:`/cookbook/install/unstable_version` * :doc:`/cookbook/logging/index` @@ -212,15 +212,12 @@ * (email) :doc:`/cookbook/email/testing` * (form) :doc:`/cookbook/form/unit_testing` -<<<<<<< HEAD * :doc:`/cookbook/upgrade/index` * :doc:`/cookbook/upgrade/patch_version` * :doc:`/cookbook/upgrade/minor_version` * :doc:`/cookbook/upgrade/major_version` -======= ->>>>>>> Added a new articule about using/installing inestable Symfony versions * :doc:`/cookbook/validation/index` * :doc:`/cookbook/validation/custom_constraint` From 719e52c589d0190cbf7bd9b3981457549fef3949 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 17 Apr 2015 17:06:20 +0200 Subject: [PATCH 0106/2667] Added the missing file extension --- cookbook/install/{unstable_versions => unstable_versions.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cookbook/install/{unstable_versions => unstable_versions.rst} (100%) diff --git a/cookbook/install/unstable_versions b/cookbook/install/unstable_versions.rst similarity index 100% rename from cookbook/install/unstable_versions rename to cookbook/install/unstable_versions.rst From 6d6303c259f77e08cafad463ddfe9cb35023a9bc Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 17 Apr 2015 17:21:32 +0200 Subject: [PATCH 0107/2667] Fixed a minor error in some index file --- cookbook/map.rst.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 797e6658e53..1faf17e7f84 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -115,7 +115,7 @@ * :doc:`/cookbook/install/index` * :doc:`/cookbook/install/upgrading` - * :doc:`/cookbook/install/unstable_version` + * :doc:`/cookbook/install/unstable_versions` * :doc:`/cookbook/logging/index` From 724c17ff1dfaca92f410700675ed78c966b59cea Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 17 Apr 2015 17:40:11 +0200 Subject: [PATCH 0108/2667] Simplified instructions --- cookbook/install/unstable_versions.rst | 30 +++++++------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/cookbook/install/unstable_versions.rst b/cookbook/install/unstable_versions.rst index b93403e4d19..9df8fe95085 100644 --- a/cookbook/install/unstable_versions.rst +++ b/cookbook/install/unstable_versions.rst @@ -41,41 +41,27 @@ Symfony application to an unstable framework version. Suppose again that Symfony First, open the ``composer.json`` file located in the root directory of your project. Then, edit the value of the version defined for the ``symfony/symfony`` -dependency: +dependency as follows: .. code-block:: json { "require": { // ... - "symfony/symfony" : "2.7.*" + "symfony/symfony" : "2.7.*@dev" } } -Then, before updating your dependencies, make sure that the project configuration -allows to install unstable versions. If the ``composer.json`` file contains a -``minimum-stability`` option, change its value to ``dev``. If that option doesn't -exist, add it as follows: - -.. code-block:: json - - { - "require": { - // ... - "symfony/symfony" : "2.7.*" - }, - "minimum-stability": "dev" - } - -If you prefer to test a Symfony beta version, replace the ``dev`` value of the -``minimum-stability`` option by ``beta``. - -Then, open a command console, enter your project directory and execute the following command to update your project dependencies: +Then, open a command console, enter your project directory and execute the following +command to update your project dependencies: .. code-block:: bash $ composer update +If you prefer to test a Symfony beta version, replace the ``"2.7.*@dev"`` constraint +by ``"2.7.*@beta1"`` (or any other beta number). + .. tip:: If you use Git to manage the project's code, it's a good practice to create @@ -87,7 +73,7 @@ Then, open a command console, enter your project directory and execute the follo $ cd projects/my_project/ $ git checkout -b testing_new_symfony - // update composer.json configuration + // ... update composer.json configuration $ composer update // ... after testing the new Symfony version From e1f621e8617fd467e05727dc2b705a2267d4fc72 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 17 Apr 2015 17:42:42 +0200 Subject: [PATCH 0109/2667] Added a new entry in the redirection_map --- redirection_map | 1 + 1 file changed, 1 insertion(+) diff --git a/redirection_map b/redirection_map index 7e60794f8c6..b0cb87b38ad 100644 --- a/redirection_map +++ b/redirection_map @@ -14,6 +14,7 @@ /cookbook/service_container/parentservices /components/dependency_injection/parentservices /cookbook/service_container/factories /components/dependency_injection/factories /cookbook/service_container/tags /components/dependency_injection/tags +/cookbook/upgrading /cookbook/install/upgrading /reference/configuration/mongodb /bundles/DoctrineMongoDBBundle/config /reference/YAML /components/yaml /components/dependency_injection /components/dependency_injection/introduction From caff8d2b9e379588153020ca7d1dba4f60db4068 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 17 Apr 2015 17:44:41 +0200 Subject: [PATCH 0110/2667] Minor rewording --- cookbook/install/unstable_versions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/install/unstable_versions.rst b/cookbook/install/unstable_versions.rst index 9df8fe95085..721cb3058ef 100644 --- a/cookbook/install/unstable_versions.rst +++ b/cookbook/install/unstable_versions.rst @@ -4,7 +4,7 @@ How to Install and Use an Unstable Symfony Version Symfony releases two new minor versions (2.5, 2.6, 2.7, etc.) per year, one in May and one in November (:doc:`see releases detail `). Testing the new Symfony versions in your projects as soon as possible is important -to ensure that they will keep working as expected. +to ensure that they will keep working after upgrading to the new version. In this article you'll learn how to install and use new Symfony versions before they are released as stable versions. From a9fee2f7f79d429793eec2e6b58cc3e9a8cb4d84 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 17 Apr 2015 17:45:55 +0200 Subject: [PATCH 0111/2667] Fixed a link to an internal document --- cookbook/install/unstable_versions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/install/unstable_versions.rst b/cookbook/install/unstable_versions.rst index 721cb3058ef..f13b0089429 100644 --- a/cookbook/install/unstable_versions.rst +++ b/cookbook/install/unstable_versions.rst @@ -2,7 +2,7 @@ How to Install and Use an Unstable Symfony Version ================================================== Symfony releases two new minor versions (2.5, 2.6, 2.7, etc.) per year, one in -May and one in November (:doc:`see releases detail `). +May and one in November (:doc:`see releases detail `). Testing the new Symfony versions in your projects as soon as possible is important to ensure that they will keep working after upgrading to the new version. From bae8043405fd77f300c71286ece15feb46dfc3ce Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 17 Apr 2015 19:45:41 +0200 Subject: [PATCH 0112/2667] Fixed the beta version constraints --- cookbook/install/unstable_versions.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cookbook/install/unstable_versions.rst b/cookbook/install/unstable_versions.rst index f13b0089429..196b270aaf8 100644 --- a/cookbook/install/unstable_versions.rst +++ b/cookbook/install/unstable_versions.rst @@ -60,7 +60,8 @@ command to update your project dependencies: $ composer update If you prefer to test a Symfony beta version, replace the ``"2.7.*@dev"`` constraint -by ``"2.7.*@beta1"`` (or any other beta number). +by ``"2.7.0-beta1"`` to install a specific beta number or ``2.7.*@beta`` to get +the most recent beta version. .. tip:: From 038caa5b8697968d0646f1dbda3f646e20dd4262 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 17 Apr 2015 23:24:05 +0200 Subject: [PATCH 0113/2667] Fixed minor issues --- cookbook/install/unstable_versions.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cookbook/install/unstable_versions.rst b/cookbook/install/unstable_versions.rst index 196b270aaf8..ba776160fff 100644 --- a/cookbook/install/unstable_versions.rst +++ b/cookbook/install/unstable_versions.rst @@ -6,14 +6,14 @@ May and one in November (:doc:`see releases detail ` +a new project to test its features. First, :doc:`install the Composer ` package manager. Then, open a command console, enter your projects directory and execute the following command: @@ -57,7 +57,7 @@ command to update your project dependencies: .. code-block:: bash - $ composer update + $ composer update symfony/symfony If you prefer to test a Symfony beta version, replace the ``"2.7.*@dev"`` constraint by ``"2.7.0-beta1"`` to install a specific beta number or ``2.7.*@beta`` to get @@ -74,9 +74,9 @@ the most recent beta version. $ cd projects/my_project/ $ git checkout -b testing_new_symfony - // ... update composer.json configuration + # ... update composer.json configuration $ composer update - // ... after testing the new Symfony version + # ... after testing the new Symfony version $ git checkout master $ git branch -D testing_new_symfony From d5f3d82dba999e1f5349335873830fb19c1940c1 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sun, 19 Apr 2015 13:44:05 +0200 Subject: [PATCH 0114/2667] Minor improvement in a command --- cookbook/install/unstable_versions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/install/unstable_versions.rst b/cookbook/install/unstable_versions.rst index ba776160fff..1b368e453c1 100644 --- a/cookbook/install/unstable_versions.rst +++ b/cookbook/install/unstable_versions.rst @@ -75,7 +75,7 @@ the most recent beta version. $ cd projects/my_project/ $ git checkout -b testing_new_symfony # ... update composer.json configuration - $ composer update + $ composer update symfony/symfony # ... after testing the new Symfony version $ git checkout master From 224c3802ee9c0ae5934b84447f98ad2707aad55e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sat, 23 May 2015 21:03:44 +0200 Subject: [PATCH 0115/2667] Fixed a lot of issues pointed by Ryan --- cookbook/install/unstable_versions.rst | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/cookbook/install/unstable_versions.rst b/cookbook/install/unstable_versions.rst index 1b368e453c1..ec8219652e0 100644 --- a/cookbook/install/unstable_versions.rst +++ b/cookbook/install/unstable_versions.rst @@ -1,10 +1,5 @@ -How to Install and Use an Unstable Symfony Version -================================================== - -Symfony releases two new minor versions (2.5, 2.6, 2.7, etc.) per year, one in -May and one in November (:doc:`see releases detail `). -Testing the new Symfony versions in your projects as soon as possible is important -to ensure that they will keep working after upgrading to the new version. +How to Install or Upgrade to the Latest, Unreleased Symfony Version +=================================================================== In this article, you'll learn how to install and use new Symfony versions before they are released as stable versions. @@ -14,7 +9,7 @@ Creating a New Project Based on an Unstable Symfony Version Suppose that Symfony 2.7 version hasn't been released yet and you want to create a new project to test its features. First, :doc:`install the Composer ` -package manager. Then, open a command console, enter your projects directory and +package manager. Then, open a command console, enter your project's directory and execute the following command: .. code-block:: bash @@ -35,9 +30,8 @@ option: Upgrading your Project to an Unstable Symfony Version ----------------------------------------------------- -Instead of creating a new empty project, in this section you'll update an existing -Symfony application to an unstable framework version. Suppose again that Symfony -2.7 version hasn't been released yet and you want to test it in your project. +Suppose again that Symfony 2.7 hasn't been released yet and you want to upgrade +an existing application to test that your project works with it. First, open the ``composer.json`` file located in the root directory of your project. Then, edit the value of the version defined for the ``symfony/symfony`` @@ -52,8 +46,8 @@ dependency as follows: } } -Then, open a command console, enter your project directory and execute the following -command to update your project dependencies: +Finally, open a command console, enter your project directory and execute the +following command to update your project dependencies: .. code-block:: bash From 1378ec9762a46f41b8e75b240259434dd44e4d98 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sat, 23 May 2015 21:38:10 +0200 Subject: [PATCH 0116/2667] Added a note about the Symfony Upgrading Guide --- cookbook/install/unstable_versions.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cookbook/install/unstable_versions.rst b/cookbook/install/unstable_versions.rst index ec8219652e0..b1d2b9abc77 100644 --- a/cookbook/install/unstable_versions.rst +++ b/cookbook/install/unstable_versions.rst @@ -57,6 +57,10 @@ If you prefer to test a Symfony beta version, replace the ``"2.7.*@dev"`` constr by ``"2.7.0-beta1"`` to install a specific beta number or ``2.7.*@beta`` to get the most recent beta version. +After upgrading the Symfony version, read the :doc:`Symfony Upgrading Guide ` +to learn how you should proceed to update your application's code in case the new +Symfony version has deprecated some of its features. + .. tip:: If you use Git to manage the project's code, it's a good practice to create From 8d360d9a4def55a99c076dd3e4b342f18d5734b8 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 10:59:45 +0200 Subject: [PATCH 0117/2667] Documented the Overridden Options of the Text type --- reference/forms/types/text.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/reference/forms/types/text.rst b/reference/forms/types/text.rst index 68e2c18875b..bd2c89ab900 100644 --- a/reference/forms/types/text.rst +++ b/reference/forms/types/text.rst @@ -9,6 +9,9 @@ The text field represents the most basic input text field. +-------------+--------------------------------------------------------------------+ | Rendered as | ``input`` ``text`` field | +-------------+--------------------------------------------------------------------+ +| Overridden | - `compound`_ | +| Options | | ++-------------+--------------------------------------------------------------------+ | Inherited | - `data`_ | | options | - `disabled`_ | | | - `empty_data`_ | @@ -27,6 +30,17 @@ The text field represents the most basic input text field. | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\TextType` | +-------------+--------------------------------------------------------------------+ +Overridden Options +------------------ + +compound +~~~~~~~~ + +**type**: ``boolean`` **default**: ``false`` + +This option specifies if the type is compound. This is independent of whether +the type actually has children. A type can be compound but not have any +children at all. Inherited Options ----------------- From e66ec5cd08c79329d95de898422afbd97f115c38 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 11:57:16 +0200 Subject: [PATCH 0118/2667] Created a compound_type file because this option is shared with lots of types --- reference/forms/types/options/compound_type.rst.inc | 8 ++++++++ reference/forms/types/text.rst | 9 +-------- 2 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 reference/forms/types/options/compound_type.rst.inc diff --git a/reference/forms/types/options/compound_type.rst.inc b/reference/forms/types/options/compound_type.rst.inc new file mode 100644 index 00000000000..2b7d4e2b986 --- /dev/null +++ b/reference/forms/types/options/compound_type.rst.inc @@ -0,0 +1,8 @@ +compound +~~~~~~~~ + +**type**: ``boolean`` **default**: ``false`` + +This option specifies if the type is compound. This is independent of whether +the type actually has children. A type can be compound but not have any +children at all. diff --git a/reference/forms/types/text.rst b/reference/forms/types/text.rst index bd2c89ab900..e265a5e6527 100644 --- a/reference/forms/types/text.rst +++ b/reference/forms/types/text.rst @@ -33,14 +33,7 @@ The text field represents the most basic input text field. Overridden Options ------------------ -compound -~~~~~~~~ - -**type**: ``boolean`` **default**: ``false`` - -This option specifies if the type is compound. This is independent of whether -the type actually has children. A type can be compound but not have any -children at all. +.. include:: /reference/forms/types/options/compound_type.rst.inc Inherited Options ----------------- From a8ad338f7ef58406ef11454a16990d272b4e592c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 11:59:35 +0200 Subject: [PATCH 0119/2667] Documented overriden options for numeric types --- reference/forms/types/integer.rst | 8 ++++++++ reference/forms/types/money.rst | 8 ++++++++ reference/forms/types/number.rst | 8 ++++++++ reference/forms/types/percent.rst | 8 ++++++++ 4 files changed, 32 insertions(+) diff --git a/reference/forms/types/integer.rst b/reference/forms/types/integer.rst index e6ea1895cb5..2b07a16ed2d 100644 --- a/reference/forms/types/integer.rst +++ b/reference/forms/types/integer.rst @@ -16,6 +16,9 @@ integers. By default, all non-integer values (e.g. 6.78) will round down +-------------+-----------------------------------------------------------------------+ | Rendered as | ``input`` ``number`` field | +-------------+-----------------------------------------------------------------------+ +| Overridden | - `compound`_ | +| Options | | ++-------------+-----------------------------------------------------------------------+ | Options | - `grouping`_ | | | - `precision`_ | | | - `rounding_mode`_ | @@ -38,6 +41,11 @@ integers. By default, all non-integer values (e.g. 6.78) will round down | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\IntegerType` | +-------------+-----------------------------------------------------------------------+ +Overridden Options +------------------ + +.. include:: /reference/forms/types/options/compound_type.rst.inc + Field Options ------------- diff --git a/reference/forms/types/money.rst b/reference/forms/types/money.rst index 29d0abedd86..03e00e6a43c 100644 --- a/reference/forms/types/money.rst +++ b/reference/forms/types/money.rst @@ -14,6 +14,9 @@ how the input and output of the data is handled. +-------------+---------------------------------------------------------------------+ | Rendered as | ``input`` ``text`` field | +-------------+---------------------------------------------------------------------+ +| Overridden | - `compound`_ | +| Options | | ++-------------+---------------------------------------------------------------------+ | Options | - `currency`_ | | | - `divisor`_ | | | - `grouping`_ | @@ -37,6 +40,11 @@ how the input and output of the data is handled. | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\MoneyType` | +-------------+---------------------------------------------------------------------+ +Overridden Options +------------------ + +.. include:: /reference/forms/types/options/compound_type.rst.inc + Field Options ------------- diff --git a/reference/forms/types/number.rst b/reference/forms/types/number.rst index ec85bc52ad4..04c79e39b7e 100644 --- a/reference/forms/types/number.rst +++ b/reference/forms/types/number.rst @@ -11,6 +11,9 @@ that you want to use for your number. +-------------+----------------------------------------------------------------------+ | Rendered as | ``input`` ``text`` field | +-------------+----------------------------------------------------------------------+ +| Overridden | - `compound`_ | +| Options | | ++-------------+----------------------------------------------------------------------+ | Options | - `grouping`_ | | | - `precision`_ | | | - `rounding_mode`_ | @@ -33,6 +36,11 @@ that you want to use for your number. | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\NumberType` | +-------------+----------------------------------------------------------------------+ +Overridden Options +------------------ + +.. include:: /reference/forms/types/options/compound_type.rst.inc + Field Options ------------- diff --git a/reference/forms/types/percent.rst b/reference/forms/types/percent.rst index 4aea0b90481..58992d84a14 100644 --- a/reference/forms/types/percent.rst +++ b/reference/forms/types/percent.rst @@ -15,6 +15,9 @@ This field adds a percentage sign "``%``" after the input box. +-------------+-----------------------------------------------------------------------+ | Rendered as | ``input`` ``text`` field | +-------------+-----------------------------------------------------------------------+ +| Overridden | - `compound`_ | +| Options | | ++-------------+-----------------------------------------------------------------------+ | Options | - `precision`_ | | | - `type`_ | +-------------+-----------------------------------------------------------------------+ @@ -36,6 +39,11 @@ This field adds a percentage sign "``%``" after the input box. | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\PercentType` | +-------------+-----------------------------------------------------------------------+ +Overridden Options +------------------ + +.. include:: /reference/forms/types/options/compound_type.rst.inc + Field Options ------------- From 7ce81913d7486859690957f99a4d936c25d3e773 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 12:01:14 +0200 Subject: [PATCH 0120/2667] Documented overridden options for hidden field --- reference/forms/types/hidden.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/reference/forms/types/hidden.rst b/reference/forms/types/hidden.rst index 6e1b8500fab..272eb853852 100644 --- a/reference/forms/types/hidden.rst +++ b/reference/forms/types/hidden.rst @@ -9,8 +9,9 @@ The hidden type represents a hidden input field. +-------------+----------------------------------------------------------------------+ | Rendered as | ``input`` ``hidden`` field | +-------------+----------------------------------------------------------------------+ -| Overriden | - `error_bubbling`_ | -| options | - `required`_ | +| Overriden | - `compound`_ | +| options | - `error_bubbling`_ | +| | - `required`_ | +-------------+----------------------------------------------------------------------+ | Inherited | - `data`_ | | options | - `error_mapping`_ | @@ -25,6 +26,8 @@ The hidden type represents a hidden input field. Overridden Options ------------------ +.. include:: /reference/forms/types/options/compound_type.rst.inc + error_bubbling ~~~~~~~~~~~~~~ From 618e11dc6764e7df447f54aaa5f3dfd49e0943d6 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 12:12:06 +0200 Subject: [PATCH 0121/2667] Documented the overridden options for "time" type --- reference/forms/types/time.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/reference/forms/types/time.rst b/reference/forms/types/time.rst index b8765688db5..53037687656 100644 --- a/reference/forms/types/time.rst +++ b/reference/forms/types/time.rst @@ -27,6 +27,8 @@ stored as a ``DateTime`` object, a string, a timestamp or an array. | | - `with_seconds`_ | +----------------------+-----------------------------------------------------------------------------+ | Overridden Options | - `by_reference`_ | +| | - `compound`_ | +| | - `data_class`_ | | | - `error_bubbling`_ | +----------------------+-----------------------------------------------------------------------------+ | Inherited | - `data`_ | @@ -139,6 +141,17 @@ by_reference The ``DateTime`` classes are treated as immutable objects. +.. include:: /reference/forms/types/options/compound_type.rst.inc + +data_class +~~~~~~~~~~ + +**default**: ``null`` + +The internal normalized representation of this type is an array, not a ``\DateTime`` +object. Therefore, the ``data_class`` option is initialized to ``null`` to avoid +the ``FormType`` object from initializing it to ``\DateTime``. + error_bubbling ~~~~~~~~~~~~~~ From 8e825d989cccc5f26d45a8b50399ce08eead271d Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 12:25:31 +0200 Subject: [PATCH 0122/2667] Documented the overridden options of the "password" type --- reference/forms/types/password.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/reference/forms/types/password.rst b/reference/forms/types/password.rst index 8382a0fc0e9..e050717680e 100644 --- a/reference/forms/types/password.rst +++ b/reference/forms/types/password.rst @@ -9,6 +9,9 @@ The ``password`` field renders an input password text box. +-------------+------------------------------------------------------------------------+ | Rendered as | ``input`` ``password`` field | +-------------+------------------------------------------------------------------------+ +| Overridden | - `trim`_ | +| Options | | ++-------------+------------------------------------------------------------------------+ | Options | - `always_empty`_ | +-------------+------------------------------------------------------------------------+ | Inherited | - `disabled`_ | @@ -28,6 +31,19 @@ The ``password`` field renders an input password text box. | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\PasswordType` | +-------------+------------------------------------------------------------------------+ +Overridden Options +------------------ + +trim +~~~~ + +**default**: ``false`` + +Unlike the rest of form types, the ``password`` type doesn't apply the +phpfunction:`trim` function to the value submitted by the user. This ensures that +the password is merged back onto the underlying object exactly as it was typed +by the user. + Field Options ------------- From e41b3cc324387a6d7649cf12b53f1c7a1d65d197 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 12:40:35 +0200 Subject: [PATCH 0123/2667] Documented overridden options for "file" type --- reference/forms/types/file.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/reference/forms/types/file.rst b/reference/forms/types/file.rst index b60666c3893..008f6ebf1fb 100644 --- a/reference/forms/types/file.rst +++ b/reference/forms/types/file.rst @@ -9,6 +9,10 @@ The ``file`` type represents a file input in your form. +-------------+---------------------------------------------------------------------+ | Rendered as | ``input`` ``file`` field | +-------------+---------------------------------------------------------------------+ +| Overridden | - `compound`_ | +| Options | - `data_class`_ | +| | - `empty_data`_ | ++-------------+---------------------------------------------------------------------+ | Inherited | - `disabled`_ | | options | - `empty_da 8000 ta`_ | | | - `error_bubbling`_ | @@ -24,6 +28,26 @@ The ``file`` type represents a file input in your form. | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\FileType` | +-------------+---------------------------------------------------------------------+ +Overridden Options +------------------ + +.. include:: /reference/forms/types/options/compound_type.rst.inc + +data_class +~~~~~~~~~~ + +**default**: :class:`Symfony\\Component\\HttpFoundation\\File\\File` + +This option sets the appropriate file-realted data mapper to be used by the type. + +empty_data +~~~~~~~~~~ + +**default**: ``null`` + +This option is set to ``null`` automatically because it is not used by the +renderd field widget. + Basic Usage ----------- From f44e971770c8aa0949b088bd53dc6ff14659828b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 12:43:30 +0200 Subject: [PATCH 0124/2667] Documented the overridden options of "date" type --- reference/forms/types/date.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/reference/forms/types/date.rst b/reference/forms/types/date.rst index 33794d61d78..becfa3ca5bb 100644 --- a/reference/forms/types/date.rst +++ b/reference/forms/types/date.rst @@ -30,6 +30,8 @@ day and year) or three select boxes (see the `widget`_ option). | | - `years`_ | +----------------------+-----------------------------------------------------------------------------+ | Overridden Options | - `by_reference`_ | +| | - `compound`_ | +| | - `data_class`_ | | | - `error_bubbling`_ | +----------------------+-----------------------------------------------------------------------------+ | Inherited | - `data`_ | @@ -125,6 +127,17 @@ by_reference The ``DateTime`` classes are treated as immutable objects. +.. include:: /reference/forms/types/options/compound_type.rst.inc + +data_class +~~~~~~~~~~ + +**default**: ``null`` + +The internal normalized representation of this type is an array, not a ``\DateTime`` +object. Therefore, the ``data_class`` option is initialized to ``null`` to avoid +the ``FormType`` object from initializing it to ``\DateTime``. + error_bubbling ~~~~~~~~~~~~~~ From 48b402b22e3a1f949466ca1728e2cad846f1acb7 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 12:46:27 +0200 Subject: [PATCH 0125/2667] Extracted the common "data_class" option explanation for data-related types --- reference/forms/types/date.rst | 9 +------ reference/forms/types/datetime.rst | 24 +++++++++++++++++++ .../types/options/data_class_date.rst.inc | 8 +++++++ reference/forms/types/time.rst | 9 +------ 4 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 reference/forms/types/options/data_class_date.rst.inc diff --git a/reference/forms/types/date.rst b/reference/forms/types/date.rst index becfa3ca5bb..8fa7fc662e6 100644 --- a/reference/forms/types/date.rst +++ b/reference/forms/types/date.rst @@ -129,14 +129,7 @@ The ``DateTime`` classes are treated as immutable objects. .. include:: /reference/forms/types/options/compound_type.rst.inc -data_class -~~~~~~~~~~ - -**default**: ``null`` - -The internal normalized representation of this type is an array, not a ``\DateTime`` -object. Therefore, the ``data_class`` option is initialized to ``null`` to avoid -the ``FormType`` object from initializing it to ``\DateTime``. +.. include:: /reference/forms/types/options/data_class_date.rst.inc error_bubbling ~~~~~~~~~~~~~~ diff --git a/reference/forms/types/datetime.rst b/reference/forms/types/datetime.rst index 01ac12fb25c..a893822faaf 100644 --- a/reference/forms/types/datetime.rst +++ b/reference/forms/types/datetime.rst @@ -15,6 +15,11 @@ the data can be a ``DateTime`` object, a string, a timestamp or an array. +----------------------+-----------------------------------------------------------------------------+ | Rendered as | single text box or three select fields | +----------------------+-----------------------------------------------------------------------------+ +| Overridden Options | - `by_reference`_ | +| | - `compound`_ | +| | - `data_class`_ | +| | - `error_bubbling`_ | ++----------------------+-----------------------------------------------------------------------------+ | Options | - `date_format`_ | | | - `date_widget`_ | | | - `days`_ | @@ -46,6 +51,25 @@ the data can be a ``DateTime`` object, a string, a timestamp or an array. | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\DateTimeType` | +----------------------+-----------------------------------------------------------------------------+ +Overridden Options +------------------ + +by_reference +~~~~~~~~~~~~ + +**default**: ``false`` + +The ``DateTime`` classes are treated as immutable objects. + +.. include:: /reference/forms/types/options/compound_type.rst.inc + +.. include:: /reference/forms/types/options/data_class_date.rst.inc + +error_bubbling +~~~~~~~~~~~~~~ + +**default**: ``false`` + Field Options ------------- diff --git a/reference/forms/types/options/data_class_date.rst.inc b/reference/forms/types/options/data_class_date.rst.inc new file mode 100644 index 00000000000..f0d5dfc5427 --- /dev/null +++ b/reference/forms/types/options/data_class_date.rst.inc @@ -0,0 +1,8 @@ +data_class +~~~~~~~~~~ + +**default**: ``null`` + +The internal normalized representation of this type is an array, not a ``\DateTime`` +object. Therefore, the ``data_class`` option is initialized to ``null`` to avoid +the ``FormType`` object from initializing it to ``\DateTime``. diff --git a/reference/forms/types/time.rst b/reference/forms/types/time.rst index 53037687656..477983f9028 100644 --- a/reference/forms/types/time.rst +++ b/reference/forms/types/time.rst @@ -143,14 +143,7 @@ The ``DateTime`` classes are treated as immutable objects. .. include:: /reference/forms/types/options/compound_type.rst.inc -data_class -~~~~~~~~~~ - -**default**: ``null`` - -The internal normalized representation of this type is an array, not a ``\DateTime`` -object. Therefore, the ``data_class`` option is initialized to ``null`` to avoid -the ``FormType`` object from initializing it to ``\DateTime``. +.. include:: /reference/forms/types/options/data_class_date.rst.inc error_bubbling ~~~~~~~~~~~~~~ From 7f8e09a1ad6c9d9d8fe453b985247c759bcc535b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 12:55:42 +0200 Subject: [PATCH 0126/2667] Removed a duplicated option explanation in "file" type --- reference/forms/types/file.rst | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/reference/forms/types/file.rst b/reference/forms/types/file.rst index 008f6ebf1fb..cae2796068e 100644 --- a/reference/forms/types/file.rst +++ b/reference/forms/types/file.rst @@ -14,8 +14,7 @@ The ``file`` type represents a file input in your form. | | - `empty_data`_ | +-------------+---------------------------------------------------------------------+ | Inherited | - `disabled`_ | -| options | - `empty_data`_ | -| | - `error_bubbling`_ | +| options | - `error_bubbling`_ | | | - `error_mapping`_ | | | - `label`_ | | | - `label_attr`_ | @@ -45,8 +44,8 @@ empty_data **default**: ``null`` -This option is set to ``null`` automatically because it is not used by the -renderd field widget. +This option determines what value the field will return when the submitted +value is empty. Basic Usage ----------- @@ -111,9 +110,6 @@ type: The default value is ``null``. -.. include:: /reference/forms/types/options/empty_data.rst.inc - :start-after: DEFAULT_PLACEHOLDER - .. include:: /reference/forms/types/options/error_bubbling.rst.inc .. include:: /reference/forms/types/options/error_mapping.rst.inc From 1dc53fa93645a3b66eeeb63fd965af262d44989e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 13:00:05 +0200 Subject: [PATCH 0127/2667] Removed again a duplicated option explanation --- reference/forms/types/file.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/reference/forms/types/file.rst b/reference/forms/types/file.rst index cae2796068e..78f7c70eb0c 100644 --- a/reference/forms/types/file.rst +++ b/reference/forms/types/file.rst @@ -105,9 +105,6 @@ type: .. include:: /reference/forms/types/options/disabled.rst.inc -.. include:: /reference/forms/types/options/empty_data.rst.inc - :end-before: DEFAULT_PLACEHOLDER - The default value is ``null``. .. include:: /reference/forms/types/options/error_bubbling.rst.inc From 32c0af1c214b9b4012d85a85b222e484ca882a4c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 13:09:09 +0200 Subject: [PATCH 0128/2667] Removed a duplicated "trim" option in the "password" type --- reference/forms/types/password.rst | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/reference/forms/types/password.rst b/reference/forms/types/password.rst index e050717680e..0f3651769c1 100644 --- a/reference/forms/types/password.rst +++ b/reference/forms/types/password.rst @@ -24,7 +24,6 @@ The ``password`` field renders an input password text box. | | - `max_length`_ | | | - `read_only`_ | | | - `required`_ | -| | - `trim`_ | +-------------+------------------------------------------------------------------------+ | Parent type | :doc:`text ` | +-------------+------------------------------------------------------------------------+ @@ -92,13 +91,3 @@ The default value is ``''`` (the empty string). .. include:: /reference/forms/types/options/read_only.rst.inc .. include:: /reference/forms/types/options/required.rst.inc - -trim -~~~~ - -**type**: ``boolean`` **default**: ``false`` - -If true, the whitespace of the submitted string value will be stripped -via the :phpfunction:`trim` function when the data is bound. This guarantees -that if a value is submitted with extra whitespace, it will be removed before -the value is merged back onto the underlying object. From 974dfefe1ff7c7cc20a518b18cddab1b7eb8de56 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 14:58:44 +0200 Subject: [PATCH 0129/2667] Fixed errors noticed by Wouter --- reference/forms/types/file.rst | 2 -- reference/forms/types/password.rst | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/reference/forms/types/file.rst b/reference/forms/types/file.rst index 78f7c70eb0c..278b1e586bf 100644 --- a/reference/forms/types/file.rst +++ b/reference/forms/types/file.rst @@ -105,8 +105,6 @@ type: .. include:: /reference/forms/types/options/disabled.rst.inc -The default value is ``null``. - .. include:: /reference/forms/types/options/error_bubbling.rst.inc .. include:: /reference/forms/types/options/error_mapping.rst.inc diff --git a/reference/forms/types/password.rst b/reference/forms/types/password.rst index 0f3651769c1..a7010fe10b8 100644 --- a/reference/forms/types/password.rst +++ b/reference/forms/types/password.rst @@ -39,7 +39,7 @@ trim **default**: ``false`` Unlike the rest of form types, the ``password`` type doesn't apply the -phpfunction:`trim` function to the value submitted by the user. This ensures that +:phpfunction:`trim` function to the value submitted by the user. This ensures that the password is merged back onto the underlying object exactly as it was typed by the user. From eb20dc8f3e84b72a244242313ed60d14e809e627 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 25 May 2015 15:38:03 +0200 Subject: [PATCH 0130/2667] Fixer minor issues --- reference/forms/types/country.rst | 2 +- reference/forms/types/currency.rst | 2 +- reference/forms/types/date.rst | 2 +- reference/forms/types/datetime.rst | 2 +- reference/forms/types/entity.rst | 4 ++-- reference/forms/types/file.rst | 2 +- reference/forms/types/integer.rst | 2 +- reference/forms/types/language.rst | 2 +- reference/forms/types/locale.rst | 2 +- reference/forms/types/money.rst | 2 +- reference/forms/types/number.rst | 2 +- reference/forms/types/options/data_class_date.rst.inc | 2 +- reference/forms/types/password.rst | 2 +- reference/forms/types/percent.rst | 2 +- reference/forms/types/repeated.rst | 2 +- reference/forms/types/text.rst | 2 +- reference/forms/types/time.rst | 2 +- reference/forms/types/timezone.rst | 2 +- 18 files changed, 19 insertions(+), 19 deletions(-) diff --git a/reference/forms/types/country.rst b/reference/forms/types/country.rst index 6efabd4dad1..9b44a076432 100644 --- a/reference/forms/types/country.rst +++ b/reference/forms/types/country.rst @@ -23,7 +23,7 @@ you should just use the ``choice`` type directly. | Rendered as | can be various tags (see :ref:`forms-reference-choice-tags`) | +-------------+-----------------------------------------------------------------------+ | Overridden | - `choices`_ | -| Options | | +| options | | +-------------+-----------------------------------------------------------------------+ | Inherited | from the :doc:`choice ` type | | options | | diff --git a/reference/forms/types/currency.rst b/reference/forms/types/currency.rst index 14a433fc188..0f86a3cee07 100644 --- a/reference/forms/types/currency.rst +++ b/reference/forms/types/currency.rst @@ -17,7 +17,7 @@ you should just use the ``choice`` type directly. | Rendered as | can be various tags (see :ref:`forms-reference-choice-tags`) | +-------------+------------------------------------------------------------------------+ | Overridden | - `choices`_ | -| Options | | +| options | | +-------------+------------------------------------------------------------------------+ | Inherited | from the :doc:`choice ` type | | options | | diff --git a/reference/forms/types/date.rst b/reference/forms/types/date.rst index 8fa7fc662e6..bd456e7f758 100644 --- a/reference/forms/types/date.rst +++ b/reference/forms/types/date.rst @@ -29,7 +29,7 @@ day and year) or three select boxes (see the `widget`_ option). | | - `widget`_ | | | - `years`_ | +----------------------+-----------------------------------------------------------------------------+ -| Overridden Options | - `by_reference`_ | +| Overridden options | - `by_reference`_ | | | - `compound`_ | | | - `data_class`_ | | | - `error_bubbling`_ | diff --git a/reference/forms/types/datetime.rst b/reference/forms/types/datetime.rst index a893822faaf..27a25e601df 100644 --- a/reference/forms/types/datetime.rst +++ b/reference/forms/types/datetime.rst @@ -15,7 +15,7 @@ the data can be a ``DateTime`` object, a string, a timestamp or an array. +----------------------+-----------------------------------------------------------------------------+ | Rendered as | single text box or three select fields | +----------------------+-----------------------------------------------------------------------------+ -| Overridden Options | - `by_reference`_ | +| Overridden options | - `by_reference`_ | | | - `compound`_ | | | - `data_class`_ | | | - `error_bubbling`_ | diff --git a/reference/forms/types/entity.rst b/reference/forms/types/entity.rst index f46229083fe..f096533830e 100644 --- a/reference/forms/types/entity.rst +++ b/reference/forms/types/entity.rst @@ -19,8 +19,8 @@ objects from the database. | | - `property`_ | | | - `query_builder`_ | +-------------+------------------------------------------------------------------+ -| Overriden | - `choice_list`_ | -| options | - `choices`_ | +| Overridden | - `choices`_ | +| options | - `choice_list`_ | +-------------+------------------------------------------------------------------+ | Inherited | from the :doc:`choice ` type: | | options | | diff --git a/reference/forms/types/file.rst b/reference/forms/types/file.rst index 278b1e586bf..3421f55fa91 100644 --- a/reference/forms/types/file.rst +++ b/reference/forms/types/file.rst @@ -10,7 +10,7 @@ The ``file`` type represents a file input in your form. | Rendered as | ``input`` ``file`` field | +-------------+---------------------------------------------------------------------+ | Overridden | - `compound`_ | -| Options | - `data_class`_ | +| options | - `data_class`_ | | | - `empty_data`_ | +-------------+---------------------------------------------------------------------+ | Inherited | - `disabled`_ | diff --git a/reference/forms/types/integer.rst b/reference/forms/types/integer.rst index 2b07a16ed2d..4a571df3652 100644 --- a/reference/forms/types/integer.rst +++ b/reference/forms/types/integer.rst @@ -17,7 +17,7 @@ integers. By default, all non-integer values (e.g. 6.78) will round down | Rendered as | ``input`` ``number`` field | +-------------+-----------------------------------------------------------------------+ | Overridden | - `compound`_ | -| Options | | +| options | | +-------------+-----------------------------------------------------------------------+ | Options | - `grouping`_ | | | - `precision`_ | diff --git a/reference/forms/types/language.rst b/reference/forms/types/language.rst index 75689b88771..dcc1879daad 100644 --- a/reference/forms/types/language.rst +++ b/reference/forms/types/language.rst @@ -24,7 +24,7 @@ you should just use the ``choice`` type directly. | Rendered as | can be various tags (see :ref:`forms-reference-choice-tags`) | +-------------+------------------------------------------------------------------------+ | Overridden | - `choices`_ | -| Options | | +| options | | +-------------+------------------------------------------------------------------------+ | Inherited | from the :doc:`choice ` type | | options | | diff --git a/reference/forms/types/locale.rst b/reference/forms/types/locale.rst index daa14e90e41..6126d577676 100644 --- a/reference/forms/types/locale.rst +++ b/reference/forms/types/locale.rst @@ -26,7 +26,7 @@ you should just use the ``choice`` type directly. | Rendered as | can be various tags (see :ref:`forms-reference-choice-tags`) | +-------------+------------------------------------------------------------------------+ | Overridden | - `choices`_ | -| Options | | +| options | | +-------------+------------------------------------------------------------------------+ | Inherited | from the :doc:`choice ` type | | options | | diff --git a/reference/forms/types/money.rst b/reference/forms/types/money.rst index 03e00e6a43c..a1a7247bf15 100644 --- a/reference/forms/types/money.rst +++ b/reference/forms/types/money.rst @@ -15,7 +15,7 @@ how the input and output of the data is handled. | Rendered as | ``input`` ``text`` field | +-------------+---------------------------------------------------------------------+ | Overridden | - `compound`_ | -| Options | | +| options | | +-------------+---------------------------------------------------------------------+ | Options | - `currency`_ | | | - `divisor`_ | diff --git a/reference/forms/types/number.rst b/reference/forms/types/number.rst index 04c79e39b7e..3dae2121195 100644 --- a/reference/forms/types/number.rst +++ b/reference/forms/types/number.rst @@ -12,7 +12,7 @@ that you want to use for your number. | Rendered as | ``input`` ``text`` field | +-------------+----------------------------------------------------------------------+ | Overridden | - `compound`_ | -| Options | | +| options | | +-------------+----------------------------------------------------------------------+ | Options | - `grouping`_ | | | - `precision`_ | diff --git a/reference/forms/types/options/data_class_date.rst.inc b/reference/forms/types/options/data_class_date.rst.inc index f0d5dfc5427..d11296e263f 100644 --- a/reference/forms/types/options/data_class_date.rst.inc +++ b/reference/forms/types/options/data_class_date.rst.inc @@ -1,7 +1,7 @@ data_class ~~~~~~~~~~ -**default**: ``null`` +**type**: ``string`` **default**: ``null`` The internal normalized representation of this type is an array, not a ``\DateTime`` object. Therefore, the ``data_class`` option is initialized to ``null`` to avoid diff --git a/reference/forms/types/password.rst b/reference/forms/types/password.rst index a7010fe10b8..1933a570851 100644 --- a/reference/forms/types/password.rst +++ b/reference/forms/types/password.rst @@ -10,7 +10,7 @@ The ``password`` field renders an input password text box. | Rendered as | ``input`` ``password`` field | +-------------+------------------------------------------------------------------------+ | Overridden | - `trim`_ | -| Options | | +| options | | +-------------+------------------------------------------------------------------------+ | Options | - `always_empty`_ | +-------------+------------------------------------------------------------------------+ diff --git a/reference/forms/types/percent.rst b/reference/forms/types/percent.rst index 58992d84a14..12aded78a3e 100644 --- a/reference/forms/types/percent.rst +++ b/reference/forms/types/percent.rst @@ -16,7 +16,7 @@ This field adds a percentage sign "``%``" after the input box. | Rendered as | ``input`` ``text`` field | +-------------+-----------------------------------------------------------------------+ | Overridden | - `compound`_ | -| Options | | +| options | | +-------------+-----------------------------------------------------------------------+ | Options | - `precision`_ | | | - `type`_ | diff --git a/reference/forms/types/repeated.rst b/reference/forms/types/repeated.rst index 8a69f565100..4556ecc88d8 100644 --- a/reference/forms/types/repeated.rst +++ b/reference/forms/types/repeated.rst @@ -20,7 +20,7 @@ accuracy. | | - `type`_ | +-------------+------------------------------------------------------------------------+ | Overridden | - `error_bubbling`_ | -| Options | | +| options | | +-------------+------------------------------------------------------------------------+ | Inherited | - `data`_ | | options | - `error_mapping`_ | diff --git a/reference/forms/types/text.rst b/reference/forms/types/text.rst index e265a5e6527..dcd598de69e 100644 --- a/reference/forms/types/text.rst +++ b/reference/forms/types/text.rst @@ -10,7 +10,7 @@ The text field represents the most basic input text field. | Rendered as | ``input`` ``text`` field | +-------------+--------------------------------------------------------------------+ | Overridden | - `compound`_ | -| Options | | +| options | | +-------------+--------------------------------------------------------------------+ | Inherited | - `data`_ | | options | - `disabled`_ | diff --git a/reference/forms/types/time.rst b/reference/forms/types/time.rst index 477983f9028..d531cc09f86 100644 --- a/reference/forms/types/time.rst +++ b/reference/forms/types/time.rst @@ -26,7 +26,7 @@ stored as a ``DateTime`` object, a string, a timestamp or an array. | | - `with_minutes`_ | | | - `with_seconds`_ | +----------------------+-----------------------------------------------------------------------------+ -| Overridden Options | - `by_reference`_ | +| Overridden options | - `by_reference`_ | | | - `compound`_ | | | - `data_class`_ | | | - `error_bubbling`_ | diff --git a/reference/forms/types/timezone.rst b/reference/forms/types/timezone.rst index b8c6fd65d2d..6764c1b62e5 100644 --- a/reference/forms/types/timezone.rst +++ b/reference/forms/types/timezone.rst @@ -19,7 +19,7 @@ you should just use the ``choice`` type directly. | Rendered as | can be various tags (see :ref:`forms-reference-choice-tags`) | +-------------+------------------------------------------------------------------------+ | Overridden | - `choices`_ | -| Options | | +| options | | +-------------+------------------------------------------------------------------------+ | Inherited | from the :doc:`choice ` type | | options | | From 0a5781b853de85e6359319bbd0d7973484f2e4d8 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 29 May 2015 08:44:22 +0200 Sub 6D40 ject: [PATCH 0131/2667] Improved the explanation about the "compoun" option --- reference/forms/types/options/compound_type.rst.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/forms/types/options/compound_type.rst.inc b/reference/forms/types/options/compound_type.rst.inc index 2b7d4e2b986..39e4abc3dfc 100644 --- a/reference/forms/types/options/compound_type.rst.inc +++ b/reference/forms/types/options/compound_type.rst.inc @@ -3,6 +3,6 @@ compound **type**: ``boolean`` **default**: ``false`` -This option specifies if the type is compound. This is independent of whether -the type actually has children. A type can be compound but not have any -children at all. +This option specifies whether the type contains child types or not. This option +is managed internally for built-in types, so there is no need to configure +it explicitly. From e14b650db730a1d378bf3ac968d5cd5454943142 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 4 Jun 2015 09:02:20 +0200 Subject: [PATCH 0132/2667] Fixed the order of the different options --- reference/forms/types/datetime.rst | 48 +++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/reference/forms/types/datetime.rst b/reference/forms/types/datetime.rst index 27a25e601df..b95d9d6cea2 100644 --- a/reference/forms/types/datetime.rst +++ b/reference/forms/types/datetime.rst @@ -15,11 +15,6 @@ the data can be a ``DateTime`` object, a string, a timestamp or an array. +----------------------+-----------------------------------------------------------------------------+ | Rendered as | single text box or three select fields | +----------------------+-----------------------------------------------------------------------------+ -| Overridden options | - `by_reference`_ | -| | - `compound`_ | -| | - `data_class`_ | -| | - `error_bubbling`_ | -+----------------------+-----------------------------------------------------------------------------+ | Options | - `date_format`_ | | | - `date_widget`_ | | | - `days`_ | @@ -38,6 +33,11 @@ the data can be a ``DateTime`` object, a string, a timestamp or an array. | | - `with_seconds`_ | | | - `years`_ | +----------------------+-----------------------------------------------------------------------------+ +| Overridden options | - `by_reference`_ | +| | - `compound`_ | +| | - `data_class`_ | +| | - `error_bubbling`_ | ++----------------------+-----------------------------------------------------------------------------+ | Inherited | - `data`_ | | options | - `disabled`_ | | | - `inherit_data`_ | @@ -51,25 +51,6 @@ the data can be a ``DateTime`` object, a string, a timestamp or an array. | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\DateTimeType` | +----------------------+-----------------------------------------------------------------------------+ -Overridden Options ------------------- - -by_reference -~~~~~~~~~~~~ - -**default**: ``false`` - -The ``DateTime`` classes are treated as immutable objects. - -.. include:: /reference/forms/types/options/compound_type.rst.inc - -.. include:: /reference/forms/types/options/data_class_date.rst.inc - -error_bubbling -~~~~~~~~~~~~~~ - -**default**: ``false`` - Field Options ------------- @@ -155,6 +136,25 @@ with the `date_widget`_ and `time_widget`_ options. .. include:: /reference/forms/types/options/years.rst.inc +Overridden Options +------------------ + +by_reference +~~~~~~~~~~~~ + +**default**: ``false`` + +The ``DateTime`` classes are treated as immutable objects. + +.. include:: /reference/forms/types/options/compound_type.rst.inc + +.. include:: /reference/forms/types/options/data_class_date.rst.inc + +error_bubbling +~~~~~~~~~~~~~~ + +**default**: ``false`` + Inherited Options ----------------- From 2b63f24222e775035710f202d36d2a13744281a3 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 4 Jun 2015 09:03:16 +0200 Subject: [PATCH 0133/2667] Fixed the order of the different options --- reference/forms/types/password.rst | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/reference/forms/types/password.rst b/reference/forms/types/password.rst index 1933a570851..75108368bb1 100644 --- a/reference/forms/types/password.rst +++ b/reference/forms/types/password.rst @@ -9,11 +9,11 @@ The ``password`` field renders an input password text box. +-------------+------------------------------------------------------------------------+ | Rendered as | ``input`` ``password`` field | +-------------+------------------------------------------------------------------------+ +| Options | - `always_empty`_ | ++-------------+------------------------------------------------------------------------+ | Overridden | - `trim`_ | | options | | +-------------+------------------------------------------------------------------------+ -| Options | - `always_empty`_ | -+-------------+------------------------------------------------------------------------+ | Inherited | - `disabled`_ | | options | - `empty_data`_ | | | - `error_bubbling`_ | @@ -30,19 +30,6 @@ The ``password`` field renders an input password text box. | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\PasswordType` | +-------------+------------------------------------------------------------------------+ -Overridden Options ------------------- - -trim -~~~~ - -**default**: ``false`` - -Unlike the rest of form types, the ``password`` type doesn't apply the -:phpfunction:`trim` function to the value submitted by the user. This ensures that -the password is merged back onto the underlying object exactly as it was typed -by the user. - Field Options ------------- @@ -59,6 +46,18 @@ Put simply, if for some reason you want to render your password field *with* the password value already entered into the box, set this to false and submit the form. +Overridden Options +------------------ + +trim +~~~~ + +**default**: ``false`` + +Unlike the rest of form types, the ``password`` type doesn't apply the +:phpfunction:`trim` function to the value submitted by the user. This ensures that +the password is merged back onto the underlying object exactly as it was typed +by the user. Inherited Options ----------------- From 62a11d55bbe7194f509e0af8fb4dd9887b3f370d Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 4 Jun 2015 09:07:55 +0200 Subject: [PATCH 0134/2667] Added missing types in overridden options --- reference/forms/types/file.rst | 4 ++-- reference/forms/types/password.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/forms/types/file.rst b/reference/forms/types/file.rst index 3421f55fa91..d383456a608 100644 --- a/reference/forms/types/file.rst +++ b/reference/forms/types/file.rst @@ -35,14 +35,14 @@ Overridden Options data_class ~~~~~~~~~~ -**default**: :class:`Symfony\\Component\\HttpFoundation\\File\\File` +**type**: ``string`` **default**: :class:`Symfony\\Component\\HttpFoundation\\File\\File` This option sets the appropriate file-realted data mapper to be used by the type. empty_data ~~~~~~~~~~ -**default**: ``null`` +**type**: ``mixed`` **default**: ``null`` This option determines what value the field will return when the submitted value is empty. diff --git a/reference/forms/types/password.rst b/reference/forms/types/password.rst index 75108368bb1..70506f5abc0 100644 --- a/reference/forms/types/password.rst +++ b/reference/forms/types/password.rst @@ -52,7 +52,7 @@ Overridden Options trim ~~~~ -**default**: ``false`` +**type**: ``boolean`` **default**: ``false`` Unlike the rest of form types, the ``password`` type doesn't apply the :phpfunction:`trim` function to the value submitted by the user. This ensures that From 596a0bcdd162883eeb45bb714e3bf0860317bca3 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 5 Jun 2015 08:34:09 +0200 Subject: [PATCH 0135/2667] Reordered two overridden options --- reference/forms/types/entity.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/forms/types/entity.rst b/reference/forms/types/entity.rst index f096533830e..fd59bef9a29 100644 --- a/reference/forms/types/entity.rst +++ b/reference/forms/types/entity.rst @@ -19,8 +19,8 @@ objects from the database. | | - `property`_ | | | - `query_builder`_ | +-------------+------------------------------------------------------------------+ -| Overridden | - `choices`_ | -| options | - `choice_list`_ | +| Overridden | - `choice_list`_ | +| options | - `choices`_ | +-------------+------------------------------------------------------------------+ | Inherited | from the :doc:`choice ` type: | | options | | From 84633dbff004f9c607ccc3a528a4e334e22bb1da Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 5 Jun 2015 08:34:49 +0200 Subject: [PATCH 0136/2667] Fixed a typo --- reference/forms/types/file.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/forms/types/file.rst b/reference/forms/types/file.rst index d383456a608..188f0d0ec59 100644 --- a/reference/forms/types/file.rst +++ b/reference/forms/types/file.rst @@ -37,7 +37,7 @@ data_class **type**: ``string`` **default**: :class:`Symfony\\Component\\HttpFoundation\\File\\File` -This option sets the appropriate file-realted data mapper to be used by the type. +This option sets the appropriate file-related data mapper to be used by the type. empty_data ~~~~~~~~~~ From 396302b3185aa8afe506fecab8633ee377a778f6 Mon Sep 17 00:00:00 2001 From: Diego Gullo Date: Sat, 6 Jun 2015 12:59:48 +0100 Subject: [PATCH 0137/2667] Added description on how to enable the security:check command through composer --- book/security.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/book/security.rst b/book/security.rst index 2f3ed0f30a6..bf67ee07743 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1352,6 +1352,13 @@ FriendsOfPHP organization. any of your dependencies is affected by a known security vulnerability. Therefore, you can easily integrate it in your build process. + To enable the ``security:check`` command, you need to add the + `sensio distribution bundle`_ in your composer dependencies. + +.. code-block:: bash + + $ composer require 'sensio/distribution-bundle:~3' + Final Words ----------- @@ -1381,3 +1388,4 @@ Learn More from the Cookbook .. _`online tool`: https://www.dailycred.com/blog/12/bcrypt-calculator .. _`frameworkextrabundle documentation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html .. _`security advisories database`: https://github.com/FriendsOfPHP/security-advisories +.. _`sensio distribution bundle`: https://packagist.org/packages/sensio/distribution-bundle From 5aa7044df77f645c4ec64d585299eb65bc1d7ed5 Mon Sep 17 00:00:00 2001 From: Diego Gullo Date: Sun, 7 Jun 2015 15:03:52 +0100 Subject: [PATCH 0138/2667] Improved security:check command description --- book/security.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/book/security.rst b/book/security.rst index bf67ee07743..d84fac51825 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1352,12 +1352,14 @@ FriendsOfPHP organization. any of your dependencies is affected by a known security vulnerability. Therefore, you can easily integrate it in your build process. +.. note:: + To enable the ``security:check`` command, you need to add the `sensio distribution bundle`_ in your composer dependencies. .. code-block:: bash - $ composer require 'sensio/distribution-bundle:~3' + $ composer require 'sensio/distribution-bundle' Final Words ----------- From aa0dff43591d6f3ede5557a7e0f67595df910fe5 Mon Sep 17 00:00:00 2001 From: Diego Gullo Date: Sun, 7 Jun 2015 18:33:54 +0100 Subject: [PATCH 0139/2667] Indented code block in security:check command description --- book/security.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/security.rst b/book/security.rst index d84fac51825..aa86bec3af9 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1357,9 +1357,9 @@ FriendsOfPHP organization. To enable the ``security:check`` command, you need to add the `sensio distribution bundle`_ in your composer dependencies. -.. code-block:: bash + .. code-block:: bash - $ composer require 'sensio/distribution-bundle' + $ composer require 'sensio/distribution-bundle' Final Words ----------- From 2e2068ad262d8b08bd61e1ed9d9a77b7760322de Mon Sep 17 00:00:00 2001 From: Iltar van der Berg Date: Mon, 8 Jun 2015 12:04:16 +0200 Subject: [PATCH 0140/2667] Split Security into Authentication & Authorization --- cookbook/map.rst.inc | 19 +++++++++++-------- cookbook/security/index.rst | 23 ++++++++++++++++------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 2c4d8eb94f6..c3d82de14bc 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -143,27 +143,30 @@ * :doc:`/cookbook/routing/redirect_trailing_slash` * :doc:`/cookbook/routing/extra_information` -* :doc:`/cookbook/security/index` +* :doc:`Authentication ` * :doc:`/cookbook/security/form_login_setup` * :doc:`/cookbook/security/entity_provider` * :doc:`/cookbook/security/remember_me` * :doc:`/cookbook/security/impersonating_user` - * :doc:`/cookbook/security/voters` - * :doc:`/cookbook/security/voters_data_permission` - * :doc:`/cookbook/security/acl` - * :doc:`/cookbook/security/acl_advanced` - * :doc:`/cookbook/security/force_https` * :doc:`/cookbook/security/form_login` - * :doc:`/cookbook/security/securing_services` * :doc:`/cookbook/security/custom_provider` * :doc:`/cookbook/security/custom_authentication_provider` * :doc:`/cookbook/security/pre_authenticated` * :doc:`/cookbook/security/target_path` * :doc:`/cookbook/security/csrf_in_login_form` - * :doc:`/cookbook/security/access_control` * :doc:`/cookbook/security/multiple_user_providers` +* :doc:`Authorization ` + + * :doc:`/cookbook/security/voters` + * :doc:`/cookbook/security/voters_data_permission` + * :doc:`/cookbook/security/acl` + * :doc:`/cookbook/security/acl_advanced` + * :doc:`/cookbook/security/force_https` + * :doc:`/cookbook/security/securing_services` + * :doc:`/cookbook/security/access_control` + * **Serializer** * :doc:`/cookbook/serializer` diff --git a/cookbook/security/index.rst b/cookbook/security/index.rst index 5bf643c10e8..a3d27e09dd4 100644 --- a/cookbook/security/index.rst +++ b/cookbook/security/index.rst @@ -1,6 +1,8 @@ Security ======== +* Authentication + .. toctree:: :maxdepth: 2 @@ -8,17 +10,24 @@ Security entity_provider remember_me impersonating_user - voters - voters_data_permission - acl - acl_advanced - force_https form_login - securing_services custom_provider custom_authentication_provider pre_authenticated target_path csrf_in_login_form - access_control multiple_user_providers + + +* Authorization + +.. toctree:: + :maxdepth: 2 + + voters + voters_data_permission + acl + acl_advanced + force_https + securing_services + access_control From b881bea6515c6e880849d0e7ebb9ec7668c96428 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 8 Jun 2015 17:41:36 +0200 Subject: [PATCH 0141/2667] Remove the Propel book chapter and explain why we do that --- book/propel.rst | 539 ++---------------------------------------------- 1 file changed, 13 insertions(+), 526 deletions(-) diff --git a/book/propel.rst b/book/propel.rst index 53b953dd75d..c8d1e196955 100644 --- a/book/propel.rst +++ b/book/propel.rst @@ -4,529 +4,16 @@ Databases and Propel ==================== -One of the most common and challenging tasks for any application -involves persisting and reading information to and from a database. Symfony -does not come integrated with any ORMs but the Propel integration is easy. -To install Propel, read `Working With Symfony2`_ on the Propel documentation. - -A Simple Example: A Product ---------------------------- - -In this section, you'll configure your database, create a ``Product`` object, -persist it to the database and fetch it back out. - -Configuring the Database -~~~~~~~~~~~~~~~~~~~~~~~~ - -Before you can start, you'll need to configure your database connection -information. By convention, this information is usually configured in an -``app/config/parameters.yml`` file: - -.. code-block:: yaml - - # app/config/parameters.yml - parameters: - database_driver: mysql - database_host: localhost - database_name: test_project - database_user: root - database_password: password - database_charset: UTF8 - -These parameters defined in ``parameters.yml`` can now be included in the -configuration file (``config.yml``): - -.. code-block:: yaml - - propel: - dbal: - driver: "%database_driver%" - user: "%database_user%" - password: "%database_password%" - dsn: "%database_driver%:host=%database_host%;dbname=%database_name%;charset=%database_charset%" - -.. note:: - - Defining the configuration via ``parameters.yml`` is a - :ref:`Symfony Framework Best Practice `, - feel free to do it differently if that suits your application better. - -Now that Propel knows about your database, it can create the database for -you: - -.. code-block:: bash - - $ php app/console propel:database:create - -.. note:: - - In this example, you have one configured connection, named ``default``. If - you want to configure more than one connection, read the - `PropelBundle configuration section`_. - -Creating a Model Class -~~~~~~~~~~~~~~~~~~~~~~ - -In the Propel world, ActiveRecord classes are known as **models** because classes -generated by Propel contain some business logic. - -.. note:: - - For people who use Symfony with Doctrine2, **models** are equivalent to - **entities**. - -Suppose you're building an application where products need to be displayed. -First, create a ``schema.xml`` file inside the ``Resources/config`` directory -of your AppBundle: - -.. code-block:: xml - - - - - - - - - - - - -
-
- -Building the Model -~~~~~~~~~~~~~~~~~~ - -After creating your ``schema.xml``, generate your model from it by running: - -.. code-block:: bash - - $ php app/console propel:model:build - -This generates each model class to quickly develop your application in the -``Model/`` directory of the AppBundle bundle. - -Creating the Database Tables/Schema -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now you have a usable ``Product`` class and all you need to persist it. Of -course, you don't yet have the corresponding ``product`` table in your -database. Fortunately, Propel can automatically create all the database tables -needed for every known model in your application. To do this, run: - -.. code-block:: bash - - $ php app/console propel:sql:build - $ php app/console propel:sql:insert --force - -Your database now has a fully-functional ``product`` table with columns that -match the schema you've specified. - -.. tip:: - - You can run the last three commands combined by using the following - command: - - .. code-block:: bash - - $ php app/console propel:build --insert-sql - -Persisting Objects to the Database -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now that you have a ``Product`` object and corresponding ``product`` table, -you're ready to persist data to the database. From inside a controller, this -is pretty easy. Add the following method to the ``ProductController`` of the -bundle:: - - // src/AppBundle/Controller/ProductController.php - - // ... - use AppBundle\Model\Product; - use Symfony\Component\HttpFoundation\Response; - - class ProductController extends Controller - { - public function createAction() - { - $product = new Product(); - $product->setName('A Foo Bar'); - $product->setPrice(19.99); - $product->setDescription('Lorem ipsum dolor'); - - $product->save(); - - return new Response('Created product id '.$product->getId()); - } - } - -In this piece of code, you instantiate and work with the ``$product`` object. -When you call the ``save()`` method on it, you persist it to the database. No -need to use other services, the object knows how to persist itself. - -.. note:: - - If you're following along with this example, you'll need to create a - :doc:`route ` that points to this action to see it in action. - -Fetching Objects from the Database -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Fetching an object back from the database is even easier. For example, suppose -you've configured a route to display a specific ``Product`` based on its ``id`` -value:: - - // src/AppBundle/Controller/ProductController.php - - // ... - use AppBundle\Model\ProductQuery; - - class ProductController extends Controller - { - // ... - - public function showAction($id) - { - $product = ProductQuery::create()->findPk($id); - - if (!$product) { - throw $this->createNotFoundException( - 'No product found for id '.$id - ); - } - - // ... do something, like pass the $product object into a template - } - } - -Updating an Object -~~~~~~~~~~~~~~~~~~ - -Once you've fetched an object from Propel, updating it is easy. Suppose you -have a route that maps a product id to an update action in a controller:: - - // src/AppBundle/Controller/ProductController.php - - // ... - use AppBundle\Model\ProductQuery; - - class ProductController extends Controller - { - // ... - - public function updateAction($id) - { - $product = ProductQuery::create()->findPk($id); - - if (!$product) { - throw $this->createNotFoundException( - 'No product found for id '.$id - ); - } - - $product->setName('New product name!'); - $product->save(); - - return $this->redirect($this->generateUrl('homepage')); - } - } - -Updating an object involves just three steps: - -#. fetching the object from Propel (line 12 - 18); -#. modifying the object (line 20); -#. saving it (line 21). - -Deleting an Object -~~~~~~~~~~~~~~~~~~ - -Deleting an object is very similar to updating, but requires a call to the -``delete()`` method on the object:: - - $product->delete(); - -Querying for Objects --------------------- - -Propel provides generated ``Query`` classes to run both basic and complex queries -without any work:: - - use AppBundle\Model\ProductQuery; - // ... - - ProductQuery::create()->findPk($id); - - ProductQuery::create() - ->filterByName('Foo') - ->findOne(); - -Imagine that you want to query for products which cost more than 19.99, ordered -from cheapest to most expensive. From inside a controller, do the following:: - - use AppBundle\Model\ProductQuery; - // ... - - $products = ProductQuery::create() - ->filterByPrice(array('min' => 19.99)) - ->orderByPrice() - ->find(); - -In one line, you get your products in a powerful object-oriented way. No need -to waste your time with SQL or whatever, Symfony offers fully object-oriented -programming and Propel respects the same philosophy by providing an awesome -abstraction layer. - -If you want to reuse some queries, you can add your own methods to the -``ProductQuery`` class:: - - // src/AppBundle/Model/ProductQuery.php - - // ... - class ProductQuery extends BaseProductQuery - { - public function filterByExpensivePrice() - { - return $this->filterByPrice(array( - 'min' => 1000, - )); - } - } - -However, note that Propel generates a lot of methods for you and a simple -``findAllOrderedByName()`` can be written without any effort:: - - use AppBundle\Model\ProductQuery; - // ... - - ProductQuery::create() - ->orderByName() - ->find(); - -Relationships/Associations --------------------------- - -Suppose that the products in your application all belong to exactly one -"category". In this case, you'll need a ``Category`` object and 9E88 a way to relate -a ``Product`` object to a ``Category`` object. - -Start by adding the ``category`` definition in your ``schema.xml``: - -.. code-block:: xml - - - - - - - - - - - - - - - - - - -
- - - - - -
-
- -Create the classes: - -.. code-block:: bash - - $ php app/console propel:model:build - -Assuming you have products in your database, you don't want to lose them. Thanks to -migrations, Propel will be able to update your database without losing existing -data. - -.. code-block:: bash - - $ php app/console propel:migration:generate-diff - $ php app/console propel:migration:migrate - -Your database has been updated, you can continue writing your application. - -Saving Related Objects -~~~~~~~~~~~~~~~~~~~~~~ - -Now, try the code in action. Imagine you're inside a controller:: - - // src/AppBundle/Controller/ProductController.php - - // ... - use AppBundle\Model\Category; - use AppBundle\Model\Product; - use Symfony\Component\HttpFoundation\Response; - - class ProductController extends Controller - { - public function createProductAction() - { - $category = new Category(); - $category->setName('Main Products'); - - $product = new Product(); - $product->setName('Foo'); - $product->setPrice(19.99); - // relate this product to the category - $product->setCategory($category); - - // save the whole - $product->save(); - - return new Response( - 'Created product id: '.$product->getId().' and category id: '.$category->getId() - ); - } - } - -Now, a single row is added to both the ``category`` and ``product`` tables. The -``product.category_id`` column for the new product is set to whatever the id is -of the new category. Propel manages the persistence of this relationship for -you. - -Fetching Related Objects -~~~~~~~~~~~~~~~~~~~~~~~~ - -When you need to fetch associated objects, your workflow looks just like it did -before: Fetch a ``$product`` object and then access its related ``Category``:: - - // src/AppBundle/Controller/ProductController.php - - // ... - use AppBundle\Model\ProductQuery; - - class ProductController extends Controller - { - public function showAction($id) - { - $product = ProductQuery::create() - ->joinWithCategory() - ->findPk($id); - - $categoryName = $product->getCategory()->getName(); - - // ... - } - } - -Note, in the above example, only one query was made. - -More Information on Associations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You will find more information on relations by reading the dedicated chapter on -`Relationships`_. - -Lifecycle Callbacks -------------------- - -Sometimes, you need to perform an action right before or after an object is -inserted, updated, or deleted. These types of actions are known as "lifecycle" -callbacks or "hooks", as they're callback methods that you need to execute -during different stages of the lifecycle of an object (e.g. the object is -inserted, updated, deleted, etc). - -To add a hook, just add a new method to the object class:: - - // src/AppBundle/Model/Product.php - - // ... - class Product extends BaseProduct - { - public function preInsert(\PropelPDO $con = null) - { - // ... do something before the object is inserted - } - } - -Propel provides the following hooks: - -``preInsert()`` - Code executed before insertion of a new object. -``postInsert()`` - Code executed after insertion of a new object. -``preUpdate()`` - Code executed before update of an existing object. -``postUpdate()`` - Code executed after update of an existing object. -``preSave()`` - Code executed before saving an object (new or existing). -``postSave()`` - Code executed after saving an object (new or existing). -``preDelete()`` - Code executed before deleting an object. -``postDelete()`` - Code executed after deleting an object. - -Behaviors ---------- - -All bundled behaviors in Propel are working with Symfony. To get more -information about how to use Propel behaviors, look at the -`Behaviors reference section`_. - -Commands --------- - -You should read the dedicated section for `Propel commands in Symfony2`_. - -.. _`Working With Symfony2`: http://propelorm.org/Propel/cookbook/symfony2/working-with-symfony2.html#installation -.. _`PropelBundle configuration section`: http://propelorm.org/Propel/cookbook/symfony2/working-with-symfony2.html#configuration -.. _`Relationships`: http://propelorm.org/Propel/documentation/04-relationships.html -.. _`Behaviors reference section`: http://propelorm.org/Propel/documentation/#behaviors-reference -.. _`Propel commands in Symfony2`: http://propelorm.org/Propel/cookbook/symfony2/working-with-symfony2#the-commands +Propel is an open-source Object-Relational Mapping (ORM) for PHP5 which +implements the `ActiveRecord pattern`_. It allows you to access your database +using a set of objects, providing a simple API for storing and retrieving data. +Propel uses PDO as an abstraction layer, and code generation to remove the +burden of runtime introspection. + +A few years ago, Propel was a very popular alternative to Doctrine. However, its +popularity has rapidly declined and that's why the Symfony book no longer includes +the Propel documentation. Read the `official PropelBundle documentation`_ to learn +how to integrate Propel into your Symfony projects. + +.. _`ActiveRecord pattern`: https://en.wikipedia.org/wiki/Active_record_pattern +.. _`official PropelBundle documentation`: https://github.com/propelorm/PropelBundle/blob/1.4/Resources/doc/index.markdown From 3487157078e921ca69ae5fa233d496512c0ecdd5 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 10 Jun 2015 08:27:08 +0200 Subject: [PATCH 0142/2667] Fixed issues reported by @xabbuh --- book/propel.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/propel.rst b/book/propel.rst index c8d1e196955..410656289cf 100644 --- a/book/propel.rst +++ b/book/propel.rst @@ -4,10 +4,10 @@ Databases and Propel ==================== -Propel is an open-source Object-Relational Mapping (ORM) for PHP5 which +Propel is an open-source Object-Relational Mapping (ORM) for PHP which implements the `ActiveRecord pattern`_. It allows you to access your database using a set of objects, providing a simple API for storing and retrieving data. -Propel uses PDO as an abstraction layer, and code generation to remove the +Propel uses PDO as an abstraction layer and code generation to remove the burden of runtime introspection. A few years ago, Propel was a very popular alternative to Doctrine. However, its From 5c872f26bc89d722bea29fe7c6b52e8a526e27e6 Mon Sep 17 00:00:00 2001 From: Diego Gullo Date: Wed, 10 Jun 2015 19:31:40 +0100 Subject: [PATCH 0143/2667] Bundle name case change --- book/security.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/security.rst b/book/security.rst index aa86bec3af9..1ef235cc361 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1355,7 +1355,7 @@ FriendsOfPHP organization. .. note:: To enable the ``security:check`` command, you need to add the - `sensio distribution bundle`_ in your composer dependencies. + `SensioDistributionBundle`_ in your composer dependencies. .. code-block:: bash @@ -1390,4 +1390,4 @@ Learn More from the Cookbook .. _`online tool`: https://www.dailycred.com/blog/12/bcrypt-calculator .. _`frameworkextrabundle documentation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html .. _`security advisories database`: https://github.com/FriendsOfPHP/security-advisories -.. _`sensio distribution bundle`: https://packagist.org/packages/sensio/distribution-bundle +.. _`SensioDistributionBundle`: https://packagist.org/packages/sensio/distribution-bundle From 52316ace6edb116c5326409e5e6a5c0413f919e6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 10 Jun 2015 21:40:24 +0200 Subject: [PATCH 0144/2667] use the jinja lexer to render Twig code --- cookbook/controller/error_pages.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/controller/error_pages.rst b/cookbook/controller/error_pages.rst index db3b5baa132..20112046b6f 100644 --- a/cookbook/controller/error_pages.rst +++ b/cookbook/controller/error_pages.rst @@ -136,7 +136,7 @@ The cause of this problem is that routing is done before security. If a 404 erro occurs, the security layer isn't loaded and thus, the ``is_granted()`` function is undefined. The solution is to add the following check before using this function: -.. code-block:: twig +.. code-block:: jinja {% if app.user and is_granted('...') %} {# ... #} From 9dcac5ae3a52ecb2dc23649896bdcefdaafd681b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Thu, 11 Jun 2015 10:49:17 +0200 Subject: [PATCH 0145/2667] remove Yoda condition Yoda conditions : a questionable best practice that makes code less error-prone, but also harder to read for people that don't share the same grammar rules as on Dagobah. Anyway, they are useful when you want to avoid confusing assignment and equality operators, but I doubt people often write `=` while meaning to write `<=`. --- components/console/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index eda67260924..02ad6d1bc58 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -196,7 +196,7 @@ level of verbosity. It is possible to print a message in a command for only a specific verbosity level. For example:: - if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { $output->writeln(...); } From d452137e9c880c7fc06effca47730e4ba9932cb0 Mon Sep 17 00:00:00 2001 From: Tony Cosentino Date: Wed, 24 Sep 2014 21:44:45 +0200 Subject: [PATCH 0146/2667] Added support for standard Forwarded header Refs https://github.com/symfony/symfony/pull/11379 --- .../request/load_balancer_reverse_proxy.rst | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/cookbook/request/load_balancer_reverse_proxy.rst b/cookbook/request/load_balancer_reverse_proxy.rst index e23e0ac01c2..88979f87477 100644 --- a/cookbook/request/load_balancer_reverse_proxy.rst +++ b/cookbook/request/load_balancer_reverse_proxy.rst @@ -7,9 +7,14 @@ an AWS Elastic Load Balancer) or a reverse proxy (e.g. Varnish for For the most part, this doesn't cause any problems with Symfony. But, when a request passes through a proxy, certain request information is sent using -special ``X-Forwarded-*`` headers. For example, instead of reading the ``REMOTE_ADDR`` -header (which will now be the IP address of your reverse proxy), the user's -true IP will be stored in an ``X-Forwarded-For`` header. +either the standard ``Forwarded`` header or non standard special ``X-Forwarded-*`` +headers. For example, instead of reading the ``REMOTE_ADDR`` header (which +will now be the IP address of your reverse proxy), the user's true IP will be +stored in a standard ``Forwarded: for="..."`` header or a non standard +``X-Forwarded-For`` header. + +.. versionadded:: 2.7 + ``Forwarded`` header support was introduced in Symfony 2.7 If you don't configure Symfony to look for these headers, you'll get incorrect information about the client's IP address, whether or not the client is connecting @@ -57,9 +62,9 @@ the IP address ``192.0.0.1`` or matches the range of IP addresses that use the CIDR notation ``10.0.0.0/8``. For more details, see the :ref:`framework.trusted_proxies ` option. -That's it! Symfony will now look for the correct ``X-Forwarded-*`` headers -to get information like the client's IP address, host, port and whether or -not the request is using HTTPS. +That's it! Symfony will now look for the correct headers to get information +like the client's IP address, host, port and whether the request is +using HTTPS. But what if the IP of my Reverse Proxy Changes Constantly! ---------------------------------------------------------- @@ -90,9 +95,12 @@ other information. My Reverse Proxy Uses Non-Standard (not X-Forwarded) Headers ------------------------------------------------------------ -Most reverse proxies store information on specific ``X-Forwarded-*`` headers. -But if your reverse proxy uses non-standard header names, you can configure +Although `rfc7239`_ recently defined a standard ``Forwarded`` header to disclose +all proxy information, most reverse proxies store information on non standard +``X-Forwarded-*`` headers. +But if your reverse proxy uses other non-standard header names, you can configure these (see ":doc:`/components/http_foundation/trusting_proxies`"). The code for doing this will need to live in your front controller (e.g. ``web/app.php``). .. _`security groups`: http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/using-elb-security-groups.html +.. _`rfc7239`: http://tools.ietf.org/html/rfc7239 From ac3689b0ecd7f120bdf1cd0f0904a67ddfcf0461 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 11 Jun 2015 13:02:01 +0200 Subject: [PATCH 0147/2667] Added support for standard Forwarded header --- cookbook/request/load_balancer_reverse_proxy.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cookbook/request/load_balancer_reverse_proxy.rst b/cookbook/request/load_balancer_reverse_proxy.rst index 88979f87477..0ade3346af9 100644 --- a/cookbook/request/load_balancer_reverse_proxy.rst +++ b/cookbook/request/load_balancer_reverse_proxy.rst @@ -7,7 +7,7 @@ an AWS Elastic Load Balancer) or a reverse proxy (e.g. Varnish for For the most part, this doesn't cause any problems with Symfony. But, when a request passes through a proxy, certain request information is sent using -either the standard ``Forwarded`` header or non standard special ``X-Forwarded-*`` +either the standard ``Forwarded`` header or non-standard special ``X-Forwarded-*`` headers. For example, instead of reading the ``REMOTE_ADDR`` header (which will now be the IP address of your reverse proxy), the user's true IP will be stored in a standard ``Forwarded: for="..."`` header or a non standard @@ -95,12 +95,12 @@ other information. My Reverse Proxy Uses Non-Standard (not X-Forwarded) Headers ------------------------------------------------------------ -Although `rfc7239`_ recently defined a standard ``Forwarded`` header to disclose -all proxy information, most reverse proxies store information on non standard +Although `RFC 7239`_ recently defined a standard ``Forwarded`` header to disclose +all proxy information, most reverse proxies store information on non-standard ``X-Forwarded-*`` headers. But if your reverse proxy uses other non-standard header names, you can configure these (see ":doc:`/components/http_foundation/trusting_proxies`"). The code for doing this will need to live in your front controller (e.g. ``web/app.php``). .. _`security groups`: http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/using-elb-security-groups.html -.. _`rfc7239`: http://tools.ietf.org/html/rfc7239 +.. _`RFC 7239`: http://tools.ietf.org/html/rfc7239 From e1479e04791583b65ad4d04e8ea4b011ddb912bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 29 May 2015 14:30:51 +0200 Subject: [PATCH 0148/2667] [Serializer] ObjectNormalizer, object_to_populate doc. Minor enhancements. --- components/serializer.rst | 92 ++++++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 20 deletions(-) diff --git a/components/serializer.rst b/components/serializer.rst index eca1925cb0a..7b2c5412d69 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -33,6 +33,9 @@ You can install the component in 2 different ways: * :doc:`Install it via Composer ` (``symfony/serializer`` on `Packagist`_); * Use the official Git repository (https://github.com/symfony/Serializer). +To use the ``ObjectNormalizer``, the :doc:`PropertyAccess component ` +must also be installed. + Usage ----- @@ -43,18 +46,18 @@ which Encoders and Normalizer are going to be available:: use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\Encoder\JsonEncoder; - use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; + use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; $encoders = array(new XmlEncoder(), new JsonEncoder()); - $normalizers = array(new GetSetMethodNormalizer()); + $normalizers = array(new ObjectNormalizer()); $serializer = new Serializer($normalizers, $encoders); -There are several normalizers available, e.g. the -:class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` or -the :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer`. +The preferred normalizer is the +:class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer`, but other +normalizers are available. To read more about them, refer to the `Normalizers`_ section of this page. All -the examples shown below use the ``GetSetMethodNormalizer``. +the examples shown below use the ``ObjectNormalizer``. Serializing an Object --------------------- @@ -145,6 +148,28 @@ needs three parameters: #. The name of the class this information will be decoded to #. The encoder used to convert that information into an array +Deserializing in an Existing Object +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The serializer can also be used to update an existing object:: + + $person = new Acme\Person(); + $person->setName('bar'); + $person->setAge(99); + $person->setSportsman(true); + + $data = << + foo + 69 + + EOF; + + $serializer->deserialize($data, 'Acme\Person', 'xml', array('object_to_populate' => $person)); + // $obj2 = Acme\Person(name: 'foo', age: '99', sportsman: true) + +This is a common need when working with an ORM. + Attributes Groups ----------------- @@ -283,8 +308,13 @@ You are now able to serialize only attributes in the groups you want:: Ignoring Attributes ------------------- +.. note:: + + Using attribute groups instead of the :method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setIgnoredAttributes` + method is considered best practice. + .. versionadded:: 2.3 - The :method:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer::setIgnoredAttributes` + The :method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setIgnoredAttributes` method was introduced in Symfony 2.3. .. versionadded:: 2.7 @@ -293,14 +323,14 @@ Ignoring Attributes As an option, there's a way to ignore attributes from the origin object. To remove those attributes use the -:method:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer::setIgnoredAttributes` +:method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setIgnoredAttributes` method on the normalizer definition:: use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\JsonEncoder; - use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; + use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - $normalizer = new GetSetMethodNormalizer(); + $normalizer = new ObjectNormalizer(); $normalizer->setIgnoredAttributes(array('age')); $encoder = new JsonEncoder(); @@ -357,11 +387,11 @@ including :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormal and :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer`:: use Symfony\Component\Serializer\Encoder\JsonEncoder - use Symfony\Component\Serializer\Normalizer\PropertyNormalizer; + use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Serializer; $nameConverter = new OrgPrefixNameConverter(); - $normalizer = new PropertyNormalizer(null, $nameConverter); + $normalizer = new ObjectNormalizer(null, $nameConverter); $serializer = new Serializer(array($normalizer), array(new JsonEncoder())); @@ -392,9 +422,9 @@ snake_case and CamelCased styles during serialization and deserialization processes:: use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; - use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; + use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - $normalizer = new GetSetMethodNormalizer(null, new CamelCaseToSnakeCaseNameConverter()); + $normalizer = new ObjectNormalizer(null, new CamelCaseToSnakeCaseNameConverter()); class Person { @@ -425,6 +455,9 @@ If you are using isser methods (methods prefixed by ``is``, like ``Acme\Person::isSportsman()``), the Serializer component will automatically detect and use it to serialize related attributes. +The ``ObjectNormalizer`` also takes care of methods starting with ``has``, ``add`` +and ``remove``. + Using Callbacks to Serialize Properties with Object Instances ------------------------------------------------------------- @@ -461,23 +494,42 @@ Normalizers There are several types of normalizers available: +:class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` + This normalizer leverages the :doc:`PropertyAccess Component ` + to read and write in the object. It means that it can access to properties + directly and trough getters, setters, hassers, adders and removers. It supports + calling the constructor during the denormalization process. + + Objects are normalized to a map of property names (method name stripped of + the "get"/"set"/"has"/"remove" prefix and converted to lower case) to property + values. + + The ``ObjectNormalizer`` is the most powerful normalizer. It is a configured + by default when using the Symfony Standard Edition with the serializer enabled. + :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` This normalizer reads the content of the class by calling the "getters" (public methods starting with "get"). It will denormalize data by calling the constructor and the "setters" (public methods starting with "set"). - Objects are serialized to a map of property names (method name stripped of + Objects are normalized to a map of property names (method name stripped of the "get" prefix and converted to lower case) to property values. :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer` This normalizer directly reads and writes public properties as well as - **private and protected** properties. Objects are normalized to a map of - property names to property values. + **private and protected** properties. It supports calling the constructor + during the denormalization process. + + Objects are normalized to a map of property names to property values. -.. versionadded:: 2.6 The - :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer` +.. versionadded:: 2.6 + The :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer` class was introduced in Symfony 2.6. +.. versionadded:: 2.7 + The :class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` + class was introduced in Symfony 2.7. + Handling Circular References ---------------------------- @@ -563,7 +615,7 @@ by custom callables. This is especially useful when serializing entities having unique identifiers:: $encoder = new JsonEncoder(); - $normalizer = new GetSetMethodNormalizer(); + $normalizer = new ObjectNormalizer(); $normalizer->setCircularReferenceHandler(function ($object) { return $object->getName(); From 825f447d0c771717ed1cfde56fa1fcdebbad0d84 Mon Sep 17 00:00:00 2001 From: Iltar van der Berg Date: Mon, 15 Jun 2015 09:33:39 +0200 Subject: [PATCH 0149/2667] Changed Authorization and Authentication to use subsections --- cookbook/security/index.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cookbook/security/index.rst b/cookbook/security/index.rst index a3d27e09dd4..479166d31ab 100644 --- a/cookbook/security/index.rst +++ b/cookbook/security/index.rst @@ -1,7 +1,8 @@ Security ======== -* Authentication +Authentication +-------------- .. toctree:: :maxdepth: 2 @@ -18,8 +19,8 @@ Security csrf_in_login_form multiple_user_providers - -* Authorization +Authorization +------------- .. toctree:: :maxdepth: 2 @@ -31,3 +32,4 @@ Security force_https securing_services access_control + From 319c081974198c055c1e352117484a8d78e0a50b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 16 Jun 2015 15:58:08 +0200 Subject: [PATCH 0150/2667] Fixed a minor grammar issue --- cookbook/request/load_balancer_reverse_proxy.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cookbook/request/load_balancer_reverse_proxy.rst b/cookbook/request/load_balancer_reverse_proxy.rst index 0ade3346af9..aa1104a7644 100644 --- a/cookbook/request/load_balancer_reverse_proxy.rst +++ b/cookbook/request/load_balancer_reverse_proxy.rst @@ -96,10 +96,12 @@ My Reverse Proxy Uses Non-Standard (not X-Forwarded) Headers ------------------------------------------------------------ Although `RFC 7239`_ recently defined a standard ``Forwarded`` header to disclose -all proxy information, most reverse proxies store information on non-standard +all proxy information, most reverse proxies store information in non-standard ``X-Forwarded-*`` headers. + But if your reverse proxy uses other non-standard header names, you can configure these (see ":doc:`/components/http_foundation/trusting_proxies`"). + The code for doing this will need to live in your front controller (e.g. ``web/app.php``). .. _`security groups`: http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/using-elb-security-groups.html From 8928049d4d23e7ef68fa9bdc02706cb67efeeb84 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 16 Jun 2015 22:24:24 +0200 Subject: [PATCH 0151/2667] Fixed a minor grammar issue --- cookbook/request/load_balancer_reverse_proxy.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/request/load_balancer_reverse_proxy.rst b/cookbook/request/load_balancer_reverse_proxy.rst index aa1104a7644..f0c864d2a59 100644 --- a/cookbook/request/load_balancer_reverse_proxy.rst +++ b/cookbook/request/load_balancer_reverse_proxy.rst @@ -14,7 +14,7 @@ stored in a standard ``Forwarded: for="..."`` header or a non standard ``X-Forwarded-For`` header. .. versionadded:: 2.7 - ``Forwarded`` header support was introduced in Symfony 2.7 + ``Forwarded`` header support was introduced in Symfony 2.7. If you don't configure Symfony to look for these headers, you'll get incorrect information about the client's IP address, whether or not the client is connecting From bab8430775424b8c7a98d12e76e3c2d05c16b6b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 29 May 2015 15:12:55 +0200 Subject: [PATCH 0152/2667] [Serializer] Updated the cookbook. --- components/serializer.rst | 2 + cookbook/serializer.rst | 132 +++++++++++++++++++++++++++++++++++--- 2 files changed, 125 insertions(+), 9 deletions(-) diff --git a/components/serializer.rst b/components/serializer.rst index eca1925cb0a..319ff81c483 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -145,6 +145,8 @@ needs three parameters: #. The name of the class this information will be decoded to #. The encoder used to convert that information into an array +.. _component-serializer-attributes-groups: + Attributes Groups ----------------- diff --git a/cookbook/serializer.rst b/cookbook/serializer.rst index 1f578cd3738..28c63e05b87 100644 --- a/cookbook/serializer.rst +++ b/cookbook/serializer.rst @@ -11,8 +11,6 @@ tools that you can leverage for your solution. In fact, before you start, get familiar with the serializer, normalizers and encoders by reading the :doc:`Serializer Component`. -You should also check out the `JMSSerializerBundle`_, which expands on the -functionality offered by Symfony's core serializer. Activating the Serializer ------------------------- @@ -48,23 +46,48 @@ it in your configuration: $container->loadFromExtension('framework', array( // ... 'serializer' => array( - 'enabled' => true + 'enabled' => true, ), )); +Using the Serializer Service +---------------------------- + +Once enabled, the ``serializer`` service can be injected in any service where +you need it or it can be used in a controller like the following:: + + // src/AppBundle/Controller/DefaultController.php + namespace AppBundle\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\Controller; + + class DefaultController extends Controller + { + public function indexAction() + { + $serializer = $this->get('serializer'); + + // ... + } + } + Adding Normalizers and Encoders ------------------------------- +.. versionadded:: 2.7 + The :class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` + is enabled by default in Symfony 2.7. In prior versions, you need to load + your own normalizer. + Once enabled, the ``serializer`` service will be available in the container and will be loaded with two :ref:`encoders` (:class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder` and -:class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder`) -but no :ref:`normalizers`, meaning you'll -need to load your own. +:class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder`) and the +:ref:`ObjectNormalizer normalizer `. You can load normalizers and/or encoders by tagging them as -:ref:`serializer.normalizer` and -:ref:`serializer.encoder`. It's also +:ref:`serializer.normalizer ` and +:ref:`serializer.encoder `. It's also possible to set the priority of the tag in order to decide the matching order. Here is an example on how to load the @@ -101,4 +124,95 @@ Here is an example on how to load the $definition->addTag('serializer.normalizer'); $container->setDefinition('get_set_method_normalizer', $definition); -.. _JMSSerializerBundle: http://jmsyst.com/bundles/JMSSerializerBundle +Using Serialization Groups Annotations +-------------------------------------- + +.. versionadded:: 2.7 + Support for serialization groups was introduced in Symfony 2.7. + +Enable :ref:`serialization groups annotation ` +with the following configuration: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + framework: + # ... + serializer: + enable_annotations: true + + .. code-block:: xml + + + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('framework', array( + // ... + 'serializer' => array( + 'enable_annotations' => true, + ), + )); + +Enabling the Metadata Cache +--------------------------- + +.. versionadded:: 2.7 + Serializer was introduced in Symfony 2.7. + +Metadata used by the Serializer component such as groups can be cached to +enhance application performance. Any service implementing the ``Doctrine\Common\Cache\Cache`` +interface can be used. + +A service leveraging `APCu`_ (and APC for PHP < 5.5) is built-in. + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config_prod.yml + framework: + # ... + serializer: + cache: serializer.mapping.cache.apc + + .. code-block:: xml + + + + + + + + .. code-block:: php + + // app/config/config_prod.php + $container->loadFromExtension('framework', array( + // ... + 'serializer' => array( + 'cache' => 'serializer.mapping.cache.apc', + ), + )); + +Going Further with the Serializer Component +------------------------------------------- + +`DunglasApiBundle`_ provides an API system supporting `JSON-LD`_ and `Hydra Core Vocabulary`_ +hypermedia formats. It is built on top of the Symfony Framework and its Serializer +component. It provides custom normalizers and a custom encoder, custom metadata +and a caching system. + +If you want to leverage the full power of the Symfony Serializer component, +take a look at how this bundle works. + +.. _`APCu`: https://github.com/krakjoe/apcu +.. _`DunglasApiBundle`: https://github.com/dunglas/DunglasApiBundle +.. _`JSON-LD`: http://json-ld.org +.. _`Hydra Core Vocabulary`: http://hydra-cg.com From ce6b808ef4d33740fe8f38cf571569f93d5113d8 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Mon, 15 Jun 2015 09:03:01 +0200 Subject: [PATCH 0153/2667] Added some more docs about the remember me feature --- cookbook/security/remember_me.rst | 42 ++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/cookbook/security/remember_me.rst b/cookbook/security/remember_me.rst index 8fde1e9083b..1855a58e737 100644 --- a/cookbook/security/remember_me.rst +++ b/cookbook/security/remember_me.rst @@ -16,17 +16,27 @@ the session lasts using a cookie with the ``remember_me`` firewall option: # app/config/security.yml firewalls: - main: + default: + # ... remember_me: key: "%secret%" lifetime: 604800 # 1 week in seconds path: / + # by default, the feature is enabled by checking a + # checkbox in the login form (see below), uncomment the + # below lines to always enable it. + #always_remember_me: true .. code-block:: xml - + + + + @@ -40,11 +50,16 @@ the session lasts using a cookie with the ``remember_me`` firewall option: // app/config/security.php $container->loadFromExtension('security', array( 'firewalls' => array( - 'main' => array( + 'default' => array( + // ... 'remember_me' => array( 'key' => '%secret%', 'lifetime' => 604800, // 1 week in seconds 'path' => '/', + // by default, the feature is enabled by checking a + // checkbox in the login form (see below), uncomment + // the below lines to always enable it. + //'always_remember_me' => true, ), ), ), @@ -94,21 +109,30 @@ The ``remember_me`` firewall defines the following configuration options: "Remember Me" feature is always enabled, regardless of the desire of the end user. +``token_provider`` (default value: ``null``) + Defines the service id of a token provider to use. By default, tokens are + stored in a cookie. For example, you might want to store the token in a + database, to not have a (hashed) version of the password in a cookie. The + DoctrineBridge comes with a + ``Symfony\Bridge\Doctrine\Security\RememberMe\DoctrineTokenProvider`` that + you can use. + Forcing the User to Opt-Out of the Remember Me Feature ------------------------------------------------------ It's a good idea to provide the user with the option to use or not use the remember me functionality, as it will not always be appropriate. The usual way of doing this is to add a checkbox to the login form. By giving the checkbox -the name ``_remember_me``, the cookie will automatically be set when the checkbox -is checked and the user successfully logs in. So, your specific login form -might ultimately look like this: +the name ``_remember_me`` (or the name you configured using ``remember_me_parameter``), +the cookie will automatically be set when the checkbox is checked and the user +successfully logs in. So, your specific login form might ultimately look like +this: .. configuration-block:: .. code-block:: html+jinja - {# src/Acme/SecurityBundle/Resources/views/Security/login.html.twig #} + {# app/Resources/views/security/login.html.twig #} {% if error %}
{{ error.message }}
{% endif %} @@ -128,7 +152,7 @@ might ultimately look like this: .. code-block:: html+php - +
getMessage() ?>
@@ -150,7 +174,7 @@ might ultimately look like this: The user will then automatically be logged in on subsequent visits while the cookie r F438 emains valid. -Forcing the User to Re-authenticate before Accessing certain Resources +Forcing the User to Re-Authenticate before Accessing certain Resources ---------------------------------------------------------------------- When the user returns to your site, they are authenticated automatically based From 2cbd3680003133be2e63fdb5c822d7ef9f0d9faf Mon Sep 17 00:00:00 2001 From: Wouter J Date: Sun, 14 Jun 2015 18:13:44 +0200 Subject: [PATCH 0154/2667] Quick review of Form login chapter --- cookbook/security/form_login_setup.rst | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/cookbook/security/form_login_setup.rst b/cookbook/security/form_login_setup.rst index 0b053920039..bb4d4fa6afe 100644 --- a/cookbook/security/form_login_setup.rst +++ b/cookbook/security/form_login_setup.rst @@ -39,8 +39,8 @@ First, enable form login under your firewall: @@ -82,7 +82,6 @@ bundle:: namespace AppBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; - use Symfony\Component\HttpFoundation\Request; class SecurityController extends Controller { @@ -126,6 +125,7 @@ under your ``form_login`` configuration (``/login`` and ``/login_check``): login_route: path: /login defaults: { _controller: AppBundle:Security:login } + login_check: path: /login_check # no controller is bound to this route @@ -159,6 +159,7 @@ under your ``form_login`` configuration (``/login`` and ``/login_check``): $collection->add('login_route', new Route('/login', array( '_controller' => 'AppBundle:Security:login', ))); + $collection->add('login_check', new Route('/login_check', array())); // no controller is bound to this route // as it's handled by the Security system @@ -320,12 +321,13 @@ see :doc:`/cookbook/security/form_login`. .. _book-security-common-pitfalls: -Avoid common Pitfalls +Avoid Common Pitfalls --------------------- When setting up your login form, watch out for a few common pitfalls. -**1. Create the correct routes** +1. Create the Correct Routes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ First, be sure that you've defined the ``/login`` and ``/login_check`` routes correctly and that they correspond to the ``login_path`` and @@ -334,7 +336,8 @@ redirected to a 404 page instead of the login page, or that submitting the login form does nothing (you just see the login form over and over again). -**2. Be sure the login page isn't secure (redirect loop!)** +2. Be Sure the Login Page Isn't Secure (Redirect Loop!) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Also, be sure that the login page is accessible by anonymous users. For example, the following configuration - which requires the ``ROLE_ADMIN`` role for @@ -450,7 +453,8 @@ for the login page: ), ), -**3. Be sure /login_check is behind a firewall** +3. Be Sure /login_check Is Behind a Firewall +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Next, make sure that your ``check_path`` URL (e.g. ``/login_check``) is behind the firewall you're using for your form login (in this example, the single @@ -458,7 +462,8 @@ firewall matches *all* URLs, including ``/login_check``). If ``/login_check`` doesn't match any firewall, you'll receive a ``Unable to find the controller for path "/login_check"`` exception. -**4. Multiple firewalls don't share security context** +4. Multiple Firewalls Don't Share Security Context +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you're using multiple firewalls and you authenticate against one firewall, you will *not* be authenticated against any other firewalls automatically. @@ -467,7 +472,8 @@ to explicitly specify the same :ref:`reference-security-firewall-context` for different firewalls. But usually for most applications, having one main firewall is enough. -**5. Routing error pages are not covered by firewalls** +5. Routing Error Pages Are not Covered by Firewalls +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As routing is done *before* security, 404 error pages are not covered by any firewall. This means you can't check for security or even access the From a0d71ad2e18406be46fcf7dfaf16d966ad6c1aab Mon Sep 17 00:00:00 2001 From: Wouter J Date: Sun, 14 Jun 2015 18:06:36 +0200 Subject: [PATCH 0155/2667] Quick review of the remember me article --- cookbook/security/remember_me.rst | 60 +++++++++++++++++++------------ 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/cookbook/security/remember_me.rst b/cookbook/security/remember_me.rst index 8fde1e9083b..88b87357fa9 100644 --- a/cookbook/security/remember_me.rst +++ b/cookbook/security/remember_me.rst @@ -25,15 +25,24 @@ the session lasts using a cookie with the ``remember_me`` firewall option: .. code-block:: xml - - - - path = "/" - /> - - + + + + + + + + + + .. code-block:: php @@ -52,7 +61,7 @@ the session lasts using a cookie with the ``remember_me`` firewall option: The ``remember_me`` firewall defines the following configuration options: -``key`` (default value: ``null``) +``key`` (**required**) The value used to encrypt the cookie's content. It's common to use the ``secret`` value defined in the ``app/config/parameters.yml`` file. @@ -167,15 +176,18 @@ The Security component provides an easy way to do this. In addition to roles explicitly assigned to them, users are automatically given one of the following roles depending on how they are authenticated: -* ``IS_AUTHENTICATED_ANONYMOUSLY`` - automatically assigned to a user who is - in a firewall protected part of the site but who has not actually logged in. - This is only possible if anonymous access has been allowed. +``IS_AUTHENTICATED_ANONYMOUSLY`` + Automatically assigned to a user who is in a firewall protected part of the + site but who has not actually logged in. This is only possible if anonymous + access has been allowed. -* ``IS_AUTHENTICATED_REMEMBERED`` - automatically assigned to a user who - was authenticated via a remember me cookie. +``IS_AUTHENTICATED_REMEMBERED`` + Automatically assigned to a user who was authenticated via a remember me + cookie. -* ``IS_AUTHENTICATED_FULLY`` - automatically assigned to a user that has - provided their login details during the current session. +``IS_AUTHENTICATED_FULLY`` + Automatically assigned to a user that has provided their login details + during the current session. You can use these to control access beyond the explicitly assigned roles. @@ -201,11 +213,13 @@ In the following example, the action is only allowed if the user has the // ... use Symfony\Component\Security\Core\Exception\AccessDeniedException + // ... public function editAction() { - if (false === $this->get('security.context')->isGranted( - 'IS_AUTHENTICATED_FULLY' - )) { + $isFullyAuthenticated = $this->get('security.context') + ->isGranted('IS_AUTHENTICATED_FULLY'); + + if (!$isFullyAuthenticated) { throw new AccessDeniedException(); } @@ -213,11 +227,11 @@ In the following example, the action is only allowed if the user has the } You can also choose to install and use the optional JMSSecurityExtraBundle_, -which can secure your controller using annotations: - -.. code-block:: php +which can secure your controller using annotations:: + // ... use JMS\SecurityExtraBundle\Annotation\Secure; + // ... /** * @Secure(roles="IS_AUTHENTICATED_FULLY") From 2982000fad9ab64134794e03b70f8efa8ad90b28 Mon Sep 17 00:00:00 2001 From: Henry Snoek Date: Fri, 19 Jun 2015 11:18:35 +0200 Subject: [PATCH 0156/2667] 5370 rewrite sentence about fingers crossed handler action level --- cookbook/logging/monolog_email.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cookbook/logging/monolog_email.rst b/cookbook/logging/monolog_email.rst index 62df96cf7cf..087c4fa14bf 100644 --- a/cookbook/logging/monolog_email.rst +++ b/cookbook/logging/monolog_email.rst @@ -117,9 +117,10 @@ it is broken down. The ``mail`` handler is a ``fingers_crossed`` handler which means that it is only triggered when the action level, in this case ``critical`` is reached. -It then logs everything including messages below the action level. The -``critical`` level is only triggered for 5xx HTTP code errors. The ``handler`` -setting means that the output is then passed onto the ``buffered`` handler. +The ``critical`` level is only triggered for 5xx HTTP code errors. If this level +is reached once, the ``fingers_crossed`` handler will log everything, including +messages below the ``critical`` level. The ``handler`` setting means that the output +is then passed onto the ``buffered`` handler. .. tip:: From 6b1c640b4facf06d6d84d9a19a55ed0fa932cc3a Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 19 Jun 2015 09:33:59 -0400 Subject: [PATCH 0157/2667] Updating for AppBundle and purposefully *not* doing work on configure @tacman pointed out that we have a caution that says to *not* do work on configure, like make database queries, but that's exactly what we were doing. So, I replaced it with a parameter. He also noted that the final configuration block was missing and would have been helpful to him. --- cookbook/console/commands_as_services.rst | 83 ++++++++++++++++++----- 1 file changed, 67 insertions(+), 16 deletions(-) diff --git a/cookbook/console/commands_as_services.rst b/cookbook/console/commands_as_services.rst index e610ca56230..364fb3e77d4 100644 --- a/cookbook/console/commands_as_services.rst +++ b/cookbook/console/commands_as_services.rst @@ -27,8 +27,8 @@ with ``console.command``: # app/config/config.yml services: - acme_hello.command.my_command: - class: Acme\HelloBundle\Command\MyCommand + app.command.my_command: + class: AppBundle\Command\MyCommand tags: - { name: console.command } @@ -42,9 +42,8 @@ with ``console.command``: http://symfony.com/schema/dic/services/services-1.0.xsd"> - - + @@ -55,8 +54,8 @@ with ``console.command``: // app/config/config.php $container ->register( - 'acme_hello.command.my_command', - 'Acme\HelloBundle\Command\MyCommand' + 'app.command.my_command', + 'AppBundle\Command\MyCommand' ) ->addTag('console.command') ; @@ -74,12 +73,11 @@ pass one of the following as the 5th argument of ``addOption()``: By extending ``ContainerAwareCommand``, only the first is possible, because you can't access the container inside the ``configure()`` method. Instead, inject any parameter or service you need into the constructor. For example, suppose you -have some ``NameRepository`` service that you'll use to get your default value:: +store the default value in some ``%command.default_name%`` parameter:: - // src/Acme/DemoBundle/Command/GreetCommand.php - namespace Acme\DemoBundle\Command; + // src/AppBundle/Command/GreetCommand.php + namespace AppBundle\Command; - use Acme\DemoBundle\Entity\NameRepository; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -87,18 +85,20 @@ have some ``NameRepository`` service that you'll use to get your default value:: class GreetCommand extends Command { - protected $nameRepository; + protected $defaultName; - public function __construct(NameRepository $nameRepository) + public function __construct($defaultName) { - $this->nameRepository = $nameRepository; + $this->defaultName = $defaultName; parent::__construct(); } protected function configure() { - $defaultName = $this->nameRepository->findLastOne(); + // try to avoid work here (e.g. database query) + // this method is *always* called - see warning below + $defaultName = $this->defaultName; $this ->setName('demo:greet') @@ -122,7 +122,58 @@ have some ``NameRepository`` service that you'll use to get your default value:: } Now, just update the arguments of your service configuration like normal to -inject the ``NameRepository``. Great, you now have a dynamic default value! +inject the ``command.default_name`` parameter: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + parameters: + command.default_name: Javier + + services: + app.command.my_command: + class: AppBundle\Command\MyCommand + arguments: ['%command.default_name%'] + tags: + - { name: console.command } + + .. code-block:: xml + + + + + + + Javier + + + + + + + + + + .. code-block:: php + + // app/config/config.php + $container->setParameter('command.default_name', 'Javier'); + + $container + ->register( + 'app.command.my_command', + 'AppBundle\Command\MyCommand' + ) + ->addTag('console.command') + ; + +Great, you now have a dynamic default value! .. caution:: From 6d87827eca9843692441a82a7a91c4b911e4b9f5 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 19 Jun 2015 10:27:54 -0400 Subject: [PATCH 0158/2667] fixing standard and fixing missing argument in php+xml formats --- cookbook/console/commands_as_services.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cookbook/console/commands_as_services.rst b/cookbook/console/commands_as_services.rst index 364fb3e77d4..1485fde9a4d 100644 --- a/cookbook/console/commands_as_services.rst +++ b/cookbook/console/commands_as_services.rst @@ -135,7 +135,7 @@ inject the ``command.default_name`` parameter: services: app.command.my_command: class: AppBundle\Command\MyCommand - arguments: ['%command.default_name%'] + arguments: ["%command.default_name%"] tags: - { name: console.command } @@ -155,6 +155,7 @@ inject the ``command.default_name`` parameter: + %command.default_name% @@ -168,8 +169,9 @@ inject the ``command.default_name`` parameter: $container ->register( 'app.command.my_command', - 'AppBundle\Command\MyCommand' + 'AppBundle\Command\MyCommand', ) + ->setArguments(array('%command.default_name%')) ->addTag('console.command') ; From 0d0282f700175ff2b1003933ce049e8ce36d3c8b Mon Sep 17 00:00:00 2001 From: DQNEO Date: Sat, 20 Jun 2015 14:50:05 +0900 Subject: [PATCH 0159/2667] fix for Symfony 2.7 --- quick_tour/the_big_picture.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index 1399307e3ef..da37909b87a 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -89,7 +89,7 @@ the project directory and executing this command: $ cd myproject/ $ php app/console server:run -Open your browser and access the ``http://localhost:8000`` URL to see the +Open your browser and access the ``http://localhost:8000/app/example`` URL to see the welcome page of Symfony: .. image:: /images/quick_tour/welcome.png From ac0a90a3c4bd56cd9fb77ad06dd8bfff078a1878 Mon Sep 17 00:00:00 2001 From: Restless-ET Date: Mon, 22 Jun 2015 10:53:50 +0100 Subject: [PATCH 0160/2667] Update branch for new BC features --- contributing/code/patches.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing/code/patches.rst b/contributing/code/patches.rst index 1f9bf7000e5..7e6fe876fd3 100644 --- a/contributing/code/patches.rst +++ b/contributing/code/patches.rst @@ -110,7 +110,7 @@ work: * ``2.3``, if you are fixing a bug for an existing feature (you may have to choose a higher branch if the feature you are fixing was introduced in a later version); -* ``2.7``, if you are adding a new feature which is backward compatible; +* ``2.8``, if you are adding a new feature which is backward compatible; * ``master``, if you are adding a new and backward incompatible feature. .. note:: From db9fa4361587222e0dbe50f61d9a5f9e124dae7e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 22 Jun 2015 16:31:35 +0200 Subject: [PATCH 0161/2667] Made a lot of improvements suggested by reviewers --- create_framework/01-introduction.rst | 91 ++++++++----------- create_framework/02-http-foundation.rst | 38 ++------ create_framework/03-front-controller.rst | 16 ---- create_framework/04-routing.rst | 8 +- create_framework/05-templating.rst | 6 -- create_framework/06-http-kernel.rst | 4 +- .../07-separation-of-concerns.rst | 8 -- create_framework/08-unit-testing.rst | 10 +- create_framework/09-event-dispatcher.rst | 16 +--- create_framework/10-http-kernel.rst | 10 +- create_framework/11-http-kernel.rst | 12 +-- create_framework/12-dependency-injection.rst | 16 +--- 12 files changed, 62 insertions(+), 173 deletions(-) diff --git a/create_framework/01-introduction.rst b/create_framework/01-introduction.rst index 5b31758a01e..8975d349b37 100644 --- a/create_framework/01-introduction.rst +++ b/create_framework/01-introduction.rst @@ -1,14 +1,14 @@ Introduction ============ -`Symfony2`_ is a reusable set of standalone, decoupled, and cohesive PHP +`Symfony`_ is a reusable set of standalone, decoupled and cohesive PHP components that solve common web development problems. Instead of using these low-level components, you can use the ready-to-be-used -Symfony2 full-stack web framework, which is based on these components... or -you can create your very own framework. This book is about the latter. +Symfony full-stack web framework, which is based on these components... or +you can create your very own framework. This tutorial is about the latter. -Why would you like to create your own framework? +Why would you Like to Create your Own Framework? ------------------------------------------------ Why would you like to create your own framework in the first place? If you @@ -18,7 +18,7 @@ creating your own altogether. Most of the time, they are right but there are a few good reasons to start creating your own framework: * To learn more about the low level architecture of modern web frameworks in - general and about the Symfony2 full-stack framework internals in particular; + general and about the Symfony full-stack framework internals in particular; * To create a framework tailored to your very specific needs (just be sure first that your needs are really specific); @@ -34,55 +34,55 @@ a few good reasons to start creating your own framework: This tutorial will gently guide you through the creation of a web framework, one step at a time. At each step, you will have a fully-working framework that -you can use as is or as a start for your very own. We will start with simple -frameworks and more features will be added with time. Eventually, you will have +you can use as is or as a start for your very own. It will start with a simple +framework and more features will be added with time. Eventually, you will have a fully-featured full-stack web framework. And of course, each step will be the occasion to learn more about some of the -Symfony2 Components. +Symfony Components. .. tip:: If you don't have time to read the whole book, or if you want to get started fast, you can also have a look at `Silex`_, a micro-framework - based on the Symfony2 Components. The code is rather slim and it leverages - many aspects of the Symfony2 Components. - -Many modern web frameworks advertize themselves as being MVC frameworks. We -won't talk about the MVC pattern as the Symfony2 Components are able to create -any type of frameworks, not just the ones that follow the MVC architecture. -Anyway, if you have a look at the MVC semantics, this book is about how to -create the Controller part of a framework. For the Model and the View, it -really depends on your personal taste and you can use any existing -third-party libraries (Doctrine, Propel, or plain-old PDO for the Model; PHP -or Twig for the View). + based on the Symfony Components. The code is rather slim and it leverages + many aspects of the Symfony Components. + +Many modern web frameworks advertize themselves as being MVC frameworks. This +tutorial won't talk about the MVC pattern, as the Symfony Components are able to +create any type of frameworks, not just the ones that follow the MVC +architecture. Anyway, if you have a look at the MVC semantics, this book is +about how to create the Controller part of a framework. For the Model and the +View, it really depends on your personal taste and you can use any existing +third-party libraries (Doctrine, Propel or plain-old PDO for the Model; PHP or +Twig for the View). When creating a framework, following the MVC pattern is not the right goal. The main goal should be the **Separation of Concerns**; this is probably the only design pattern that you should really care about. The fundamental principles of -the Symfony2 Components are focused on the HTTP specification. As such, the -frameworks that we are going to create should be more accurately labelled as -HTTP frameworks or Request/Response frameworks. +the Symfony Components are focused on the HTTP specification. As such, the +framework that you are going to create should be more accurately labelled as a +HTTP framework or Request/Response framework. -Before we start ---------------- +Before You Start +---------------- Reading about how to create a framework is not enough. You will have to follow -along and actually type all the examples we will work on. For that, you need a -recent version of PHP (5.3.8 or later is good enough), a web server (like -Apache or NGinx), a good knowledge of PHP and an understanding of Object -Oriented programming. +along and actually type all the examples included in this tutorial. For that, +you need a recent version of PHP (5.3.9 or later is good enough), a web server +(like Apache, NGinx or PHP's built-in web server), a good knowledge of PHP and +an understanding of Object Oriented programming. -Ready to go? Let's start. +Ready to go? Read on! Bootstrapping ------------- -Before we can even think of creating our first framework, we need to talk -about some conventions: where we will store our code, how we will name our -classes, how we will reference external dependencies, etc. +Before you can even think of creating the first framework, you need to think +about some conventions: where you will store the code, how you will name the +classes, how you will reference external dependencies, etc. -To store our framework, create a directory somewhere on your machine: +To store your new framework, create a directory somewhere on your machine: .. code-block:: bash @@ -92,20 +92,9 @@ To store our framework, create a directory somewhere on your machine: Dependency Management ~~~~~~~~~~~~~~~~~~~~~ -To install the Symfony2 Components that we need for our framework, we are going +To install the Symfony Components that you need for your framework, you are going to use `Composer`_, a project dependency manager for PHP. If you don't have it -yet, `download and install`_ Composer now: - -.. code-block:: bash - - $ curl -sS https://getcomposer.org/installer | php - -Then, generate an empty ``composer.json`` file, where Composer will store the -framework dependencies: - -.. code-block:: bash - - $ composer init -n +yet, :doc:`download and install Composer ` now. Our Project ----------- @@ -114,16 +103,15 @@ Instead of creating our framework from scratch, we are going to write the same "application" over and over again, adding one abstraction at a time. Let's start with the simplest web application we can think of in PHP:: - getPathInfo(); @@ -215,8 +203,6 @@ You can also simulate a request:: With the ``Response`` class, you can easily tweak the response:: - setContent('Hello world!'); @@ -240,8 +226,6 @@ framework? Even something as simple as getting the client IP address can be insecure:: - getClientIp()) { @@ -276,8 +256,6 @@ code in production without a proxy, it becomes trivially easy to abuse your system. That's not the case with the ``getClientIp()`` method as you must explicitly trust your reverse proxies by calling ``setTrustedProxies()``:: - getClientIp(true)) { @@ -297,7 +275,7 @@ cases by yourself. Why not using a technology that already works? its dedicated :doc:`documentation `. Believe or not but we have our first framework. You can stop now if you want. -Using just the Symfony2 HttpFoundation component already allows you to write +Using just the Symfony HttpFoundation component already allows you to write better and more testable code. It also allows you to write code faster as many day-to-day problems have already been solved for you. @@ -307,14 +285,14 @@ the wheel. I've almost forgot to talk about one added benefit: using the HttpFoundation component is the start of better interoperability between all frameworks and -applications using it (like `Symfony2`_, `Drupal 8`_, `phpBB 4`_, `ezPublish +applications using it (like `Symfony`_, `Drupal 8`_, `phpBB 4`_, `ezPublish 5`_, `Laravel`_, `Silex`_, and `more`_). .. _`Twig`: http://twig.sensiolabs.com/ -.. _`Symfony2 versus Flat PHP`: http://symfony.com/doc/current/book/from_flat_php_to_symfony2.html +.. _`Symfony versus Flat PHP`: http://symfony.com/doc/current/book/from_flat_php_to_symfony2.html .. _`HTTP specification`: http://tools.ietf.org/wg/httpbis/ .. _`audited`: http://symfony.com/blog/symfony2-security-audit -.. _`Symfony2`: http://symfony.com/ +.. _`Symfony`: http://symfony.com/ .. _`Drupal 8`: http://drupal.org/ .. _`phpBB 4`: http://www.phpbb.com/ .. _`ezPublish 5`: http://ez.no/ diff --git a/create_framework/03-front-controller.rst b/create_framework/03-front-controller.rst index f53a6034b03..90e7e69dbb0 100644 --- a/create_framework/03-front-controller.rst +++ b/create_framework/03-front-controller.rst @@ -5,8 +5,6 @@ Up until now, our application is simplistic as there is only one page. To spice things up a little bit, let's go crazy and add another page that says goodbye:: - get('name', 'World'); @@ -185,8 +173,6 @@ The last thing that is repeated in each page is the call to ``setContent()``. We can convert all pages to "templates" by just echoing the content and calling the ``setContent()`` directly from the front controller script:: - Date: Mon, 22 Jun 2015 16:59:39 +0200 Subject: [PATCH 0162/2667] 5370 simplify original sentence --- cookbook/logging/monolog_email.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/logging/monolog_email.rst b/cookbook/logging/monolog_email.rst index 087c4fa14bf..5d8b44bf0e2 100644 --- a/cookbook/logging/monolog_email.rst +++ b/cookbook/logging/monolog_email.rst @@ -118,8 +118,8 @@ it is broken down. The ``mail`` handler is a ``fingers_crossed`` handler which means that it is only triggered when the action level, in this case ``critical`` is reached. The ``critical`` level is only triggered for 5xx HTTP code errors. If this level -is reached once, the ``fingers_crossed`` handler will log everything, including -messages below the ``critical`` level. The ``handler`` setting means that the output +is reached once, the ``fingers_crossed`` handler will log all messages +regardless of their level. The ``handler`` setting means that the output is then passed onto the ``buffered`` handler. .. tip:: From 108f1095f05d3010a02500fda0bef97a3a62fa1d Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 22 Jun 2015 18:05:08 +0200 Subject: [PATCH 0163/2667] Removed the numbers from the file names --- ...injection.rst => dependency-injection.rst} | 0 ...nt-dispatcher.rst => event-dispatcher.rst} | 0 ...nt-controller.rst => front-controller.rst} | 0 ...ttp-foundation.rst => http-foundation.rst} | 0 ...st => http-kernel-controller-resolver.rst} | 0 ...l.rst => http-kernel-httpkernel-class.rst} | 0 ...st => http-kernel-httpkernelinterface.rst} | 0 create_framework/index.rst | 24 +++++++++---------- .../{01-introduction.rst => introduction.rst} | 0 .../{04-routing.rst => routing.rst} | 0 ...oncerns.rst => separation-of-concerns.rst} | 0 .../{05-templating.rst => templating.rst} | 0 .../{08-unit-testing.rst => unit-testing.rst} | 0 13 files changed, 12 insertions(+), 12 deletions(-) rename create_framework/{12-dependency-injection.rst => dependency-injection.rst} (100%) rename create_framework/{09-event-dispatcher.rst => event-dispatcher.rst} (100%) rename create_framework/{03-front-controller.rst => front-controller.rst} (100%) rename create_framework/{02-http-foundation.rst => http-foundation.rst} (100%) rename create_framework/{06-http-kernel.rst => http-kernel-controller-resolver.rst} (100%) rename create_framework/{11-http-kernel.rst => http-kernel-httpkernel-class.rst} (100%) rename create_framework/{10-http-kernel.rst => http-kernel-httpkernelinterface.rst} (100%) rename create_framework/{01-introduction.rst => introduction.rst} (100%) rename create_framework/{04-routing.rst => routing.rst} (100%) rename create_framework/{07-separation-of-concerns.rst => separation-of-concerns.rst} (100%) rename create_framework/{05-templating.rst => templating.rst} (100%) rename create_framework/{08-unit-testing.rst => unit-testing.rst} (100%) diff --git a/create_framework/12-dependency-injection.rst b/create_framework/dependency-injection.rst similarity index 100% rename from create_framework/12-dependency-injection.rst rename to create_framework/dependency-injection.rst diff --git a/create_framework/09-event-dispatcher.rst b/create_framework/event-dispatcher.rst similarity index 100% rename from create_framework/09-event-dispatcher.rst rename to create_framework/event-dispatcher.rst diff --git a/create_framework/03-front-controller.rst b/create_framework/front-controller.rst similarity index 100% rename from create_framework/03-front-controller.rst rename to create_framework/front-controller.rst diff --git a/create_framework/02-http-foundation.rst b/create_framework/http-foundation.rst similarity index 100% rename from create_framework/02-http-foundation.rst rename to create_framework/http-foundation.rst diff --git a/create_framework/06-http-kernel.rst b/create_framework/http-kernel-controller-resolver.rst similarity index 100% rename from create_framework/06-http-kernel.rst rename to create_framework/http-kernel-controller-resolver.rst diff --git a/create_framework/11-http-kernel.rst b/create_framework/http-kernel-httpkernel-class.rst similarity index 100% rename from create_framework/11-http-kernel.rst rename to create_framework/http-kernel-httpkernel-class.rst diff --git a/create_framework/10-http-kernel.rst b/create_framework/http-kernel-httpkernelinterface.rst similarity index 100% rename from create_framework/10-http-kernel.rst rename to create_framework/http-kernel-httpkernelinterface.rst diff --git a/create_framework/index.rst b/create_framework/index.rst index a7291c1966d..10517e1565c 100644 --- a/create_framework/index.rst +++ b/create_framework/index.rst @@ -3,15 +3,15 @@ Create your PHP Framework .. toctree:: - 01-introduction - 02-http-foundation - 03-front-controller - 04-routing - 05-templating - 06-http-kernel - 07-separation-of-concerns - 08-unit-testing - 09-event-dispatcher - 10-http-kernel - 11-http-kernel - 12-dependency-injection + introduction + http-foundation + front-controller + routing + templating + http-kernel-controller-resolver + separation-of-concerns + unit-testing + event-dispatcher + http-kernel-httpkernelinterface + http-kernel-httpkernel-class + dependency-injection diff --git a/create_framework/01-introduction.rst b/create_framework/introduction.rst similarity index 100% rename from create_framework/01-introduction.rst rename to create_framework/introduction.rst diff --git a/create_framework/04-routing.rst b/create_framework/routing.rst similarity index 100% rename from create_framework/04-routing.rst rename to create_framework/routing.rst diff --git a/create_framework/07-separation-of-concerns.rst b/create_framework/separation-of-concerns.rst similarity index 100% rename from create_framework/07-separation-of-concerns.rst rename to create_framework/separation-of-concerns.rst diff --git a/create_framework/05-templating.rst b/create_framework/templating.rst similarity index 100% rename from create_framework/05-templating.rst rename to create_framework/templating.rst diff --git a/create_framework/08-unit-testing.rst b/create_framework/unit-testing.rst similarity index 100% rename from create_framework/08-unit-testing.rst rename to create_framework/unit-testing.rst From bca9bae2743f663332219fbb7ac2d9470b97abfc Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 22 Jun 2015 18:29:05 +0200 Subject: [PATCH 0164/2667] Updated doc references in the map.rst.inc file --- create_framework/map.rst.inc | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/create_framework/map.rst.inc b/create_framework/map.rst.inc index 8e11e1700c3..574c0f5e769 100644 --- a/create_framework/map.rst.inc +++ b/create_framework/map.rst.inc @@ -1,12 +1,12 @@ -* :doc:`/create_framework/01-introduction` -* :doc:`/create_framework/02-http-foundation` -* :doc:`/create_framework/03-front-controller` -* :doc:`/create_framework/04-routing` -* :doc:`/create_framework/05-templating` -* :doc:`/create_framework/06-http-kernel` -* :doc:`/create_framework/07-separation-of-concerns` -* :doc:`/create_framework/08-unit-testing` -* :doc:`/create_framework/09-event-dispatcher` -* :doc:`/create_framework/10-http-kernel` -* :doc:`/create_framework/11-http-kernel` -* :doc:`/create_framework/12-dependency-injection` +* :doc:`/create_framework/introduction` +* :doc:`/create_framework/http-foundation` +* :doc:`/create_framework/front-controller` +* :doc:`/create_framework/routing` +* :doc:`/create_framework/templating` +* :doc:`/create_framework/http-kernel-controller-resolver` +* :doc:`/create_framework/separation-of-concerns` +* :doc:`/create_framework/unit-testing` +* :doc:`/create_framework/event-dispatcher` +* :doc:`/create_framework/http-kernel-httpkernelinterface` +* :doc:`/create_framework/http-kernel-httpkernel-class` +* :doc:`/create_framework/dependency-injection` From aa1f56b7dd78d0e5c86f67b529d364babee87857 Mon Sep 17 00:00:00 2001 From: richardudovich Date: Thu, 11 Jun 2015 21:32:16 +1000 Subject: [PATCH 0165/2667] Reword a paragraph about service configurations --- book/service_container.rst | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/book/service_container.rst b/book/service_container.rst index cb5624d6553..945dd2fefeb 100644 --- a/book/service_container.rst +++ b/book/service_container.rst @@ -318,10 +318,13 @@ The service container is built using a single configuration resource be imported from inside this file in one way or another. This gives you absolute flexibility over the services in your application. -External service configuration can be imported in two different ways. The -first - and most common method - is via the ``imports`` directive. Later, you'll -learn about the second method, which is the flexible and preferred method -for importing service configuration from third-party bundles. +External service configuration can be imported in two different ways. The first +method, commonly used to import container configuration from the bundles you've +created - is via the ``imports`` directive. The second method, although slightly more +complex 10000 offers more flexibility and is commonly used to import third-party bundle +configuration. Read on to learn about both methods. + + .. index:: single: Service Container; Imports From a0f3f82b4a9cab9a54f38812d4da07b5ec38ee2f Mon Sep 17 00:00:00 2001 From: richardudovich Date: Mon, 22 Jun 2015 21:46:26 +1000 Subject: [PATCH 0166/2667] remove the two lines --- book/service_container.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/book/service_container.rst b/book/service_container.rst index 945dd2fefeb..c98f86ec07e 100644 --- a/book/service_container.rst +++ b/book/service_container.rst @@ -324,8 +324,6 @@ created - is via the ``imports`` directive. The second method, although slightly complex offers more flexibility and is commonly used to import third-party bundle configuration. Read on to learn about both methods. - - .. index:: single: Service Container; Imports From b63fa611b6bd02ee983a71711e0d2a27ad805efb Mon Sep 17 00:00:00 2001 From: richardudovich Date: Tue, 23 Jun 2015 00:33:32 +1000 Subject: [PATCH 0167/2667] Improved wording --- book/service_container.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/service_container.rst b/book/service_container.rst index c98f86ec07e..69b2dc082e7 100644 --- a/book/service_container.rst +++ b/book/service_container.rst @@ -322,7 +322,7 @@ External service configuration can be imported in two different ways. The first method, commonly used to import container configuration from the bundles you've created - is via the ``imports`` directive. The second method, although slightly more complex offers more flexibility and is commonly used to import third-party bundle -configuration. Read on to learn about both methods. +configuration. Read on to learn more about both methods. .. index:: single: Service Container; Imports From 7a289197bd038303046a0809ce2f73e7cfd0c4ad Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 29 May 2015 15:33:58 -0400 Subject: [PATCH 0168/2667] Adding a paragraph about updating multiple packages at once during an upgrade --- cookbook/upgrade/_update_all_packages.rst.inc | 3 +++ cookbook/upgrade/_update_dep_errors.rst.inc | 17 +++++++++++++++++ cookbook/upgrade/major_version.rst | 2 ++ cookbook/upgrade/minor_version.rst | 2 ++ 4 files changed, 24 insertions(+) create mode 100644 cookbook/upgrade/_update_dep_errors.rst.inc diff --git a/cookbook/upgrade/_update_all_packages.rst.inc b/cookbook/upgrade/_update_all_packages.rst.inc index 95e1c63f954..ad4c51c6d49 100644 --- a/cookbook/upgrade/_update_all_packages.rst.inc +++ b/cookbook/upgrade/_update_all_packages.rst.inc @@ -1,3 +1,6 @@ +Upgrading other Packages +~~~~~~~~~~~~~~~~~~~~~~~~ + You may also want to upgrade the rest of your libraries. If you've done a good job with your `version constraints`_ in ``composer.json``, you can do this safely by running: diff --git a/cookbook/upgrade/_update_dep_errors.rst.inc b/cookbook/upgrade/_update_dep_errors.rst.inc new file mode 100644 index 00000000000..d99cb2ddb39 --- /dev/null +++ b/cookbook/upgrade/_update_dep_errors.rst.inc @@ -0,0 +1,17 @@ +Dependency Errors +~~~~~~~~~~~~~~~~~ + +If you get a dependency error, it may just mean that you *also* need to upgrade +that package too. For example, Symfony 2.7 requires a higher version of Twig, +so just upgrading ``symfony/symfony`` alone may give you an error. In this +case, try adding the package in question to the ``update`` statement: + + $ composer update symfony/symfony twig/twig + +If this still doesn't work, your ``composer.json`` file may specify a version +for a library that is not compatible with the newer Symfony version. In that +case, updating that library to a newer version in ``composer.json`` may solve +the issue. + +Or, you may have deeper issues where different libraries depend on conflicting +versions of other libraries. Check your error message to debug. diff --git a/cookbook/upgrade/major_version.rst b/cookbook/upgrade/major_version.rst index 0c5107b6fed..60e35db0083 100644 --- a/cookbook/upgrade/major_version.rst +++ b/cookbook/upgrade/major_version.rst @@ -98,6 +98,8 @@ Next, use Composer to download new versions of the libraries: $ composer update symfony/symfony +.. include:: /cookbook/upgrade/_update_dep_errors.rst.inc + .. include:: /cookbook/upgrade/_update_all_packages.rst.inc .. _upgrade-major-symfony-after: diff --git a/cookbook/upgrade/minor_version.rst b/cookbook/upgrade/minor_version.rst index 55b63bb0580..78f829e2a3b 100644 --- a/cookbook/upgrade/minor_version.rst +++ b/cookbook/upgrade/minor_version.rst @@ -41,6 +41,8 @@ Next, use Composer to download new versions of the libraries: $ composer update symfony/symfony +.. include:: /cookbook/upgrade/_update_dep_errors.rst.inc + .. include:: /cookbook/upgrade/_update_all_packages.rst.inc .. _`upgrade-minor-symfony-code`: From 8a30d1429c9a9b288cc9e6a9c70326e7a3493646 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 8 Jun 2015 14:42:21 -0400 Subject: [PATCH 0169/2667] using --with-dependencies --- cookbook/upgrade/_update_dep_errors.rst.inc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cookbook/upgrade/_update_dep_errors.rst.inc b/cookbook/upgrade/_update_dep_errors.rst.inc index d99cb2ddb39..6b2eaea3c40 100644 --- a/cookbook/upgrade/_update_dep_errors.rst.inc +++ b/cookbook/upgrade/_update_dep_errors.rst.inc @@ -1,12 +1,14 @@ Dependency Errors ~~~~~~~~~~~~~~~~~ -If you get a dependency error, it may just mean that you *also* need to upgrade -that package too. For example, Symfony 2.7 requires a higher version of Twig, -so just upgrading ``symfony/symfony`` alone may give you an error. In this -case, try adding the package in question to the ``update`` statement: +If you get a dependency error, it may simply mean that you need to upgrade +other Symfony dependencies too. In that case, try the following command: - $ composer update symfony/symfony twig/twig + $ composer update symfony/symfony --with-dependencies + +This updates ``symfony/symfony`` and *all* packages that it depends on, +will include several other packages. By using tight version constraints in +``composer.json``, you can control what versions each library upgrades to. If this still doesn't work, your ``composer.json`` file may specify a version for a library that is not compatible with the newer Symfony version. In that From bb0acd02d2449e2cc80b021cc60c9d070bf240e1 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 22 Jun 2015 18:07:01 -0400 Subject: [PATCH 0170/2667] fixing bad wording --- cookbook/upgrade/_update_dep_errors.rst.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/upgrade/_update_dep_errors.rst.inc b/cookbook/upgrade/_update_dep_errors.rst.inc index 6b2eaea3c40..663c0091f58 100644 --- a/cookbook/upgrade/_update_dep_errors.rst.inc +++ b/cookbook/upgrade/_update_dep_errors.rst.inc @@ -6,8 +6,8 @@ other Symfony dependencies too. In that case, try the following command: $ composer update symfony/symfony --with-dependencies -This updates ``symfony/symfony`` and *all* packages that it depends on, -will include several other packages. By using tight version constraints in +This updates ``symfony/symfony`` and *all* packages that it depends on, which will +include several other packages. By using tight version constraints in ``composer.json``, you can control what versions each library upgrades to. If this still doesn't work, your ``composer.json`` file may specify a version From bed8824187bdb7a41f8ba9a071b597883ed4951f Mon Sep 17 00:00:00 2001 From: frne Date: Wed, 24 Dec 2014 08:56:39 +0100 Subject: [PATCH 0171/2667] Style / grammar fixes for the assetic chapter --- cookbook/assetic/asset_management.rst | 8 ++++---- cookbook/assetic/jpeg_optimize.rst | 4 ++-- cookbook/assetic/yuicompressor.rst | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cookbook/assetic/asset_management.rst b/cookbook/assetic/asset_management.rst index 121a1e9ac46..16dc5d6b568 100644 --- a/cookbook/assetic/asset_management.rst +++ b/cookbook/assetic/asset_management.rst @@ -91,9 +91,9 @@ To include JavaScript files, use the ``javascripts`` tag in any template: You can also include CSS stylesheets: see :ref:`cookbook-assetic-including-css`. -In this example, all of the files in the ``Resources/public/js/`` directory -of the AppBundle will be loaded and served from a different location. -The actual rendered tag might simply look like: +In this example, all files in the ``Resources/public/js/`` directory of the +AppBundle will be loaded and served from a different location. The actual +rendered tag might simply look like: .. code-block:: html @@ -357,7 +357,7 @@ Filters Once they're managed by Assetic, you can apply filters to your assets before they are served. This includes filters that compress the output of your assets for smaller file sizes (and better frontend optimization). Other filters -can compile JavaScript file from CoffeeScript files and process SASS into CSS. +can compile CoffeeScript files to JavaScript and process SASS into CSS. In fact, Assetic has a long list of available filters. Many of the filters do not do the work directly, but use existing third-party diff --git a/cookbook/assetic/jpeg_optimize.rst b/cookbook/assetic/jpeg_optimize.rst index 295bc27dd3a..3007dabf800 100644 --- a/cookbook/assetic/jpeg_optimize.rst +++ b/cookbook/assetic/jpeg_optimize.rst @@ -4,7 +4,7 @@ How to Use Assetic for Image Optimization with Twig Functions ============================================================= -Amongst its many filters, Assetic has four filters which can be used for on-the-fly +Among its many filters, Assetic has four filters which can be used for on-the-fly image optimization. This allows you to get the benefits of smaller file sizes without having to use an image editor to process each image. The results are cached and can be dumped for production so there is no performance hit @@ -70,7 +70,7 @@ It can now be used from a template: Removing all EXIF Data ~~~~~~~~~~~~~~~~~~~~~~ -By default, the ``jpegoptim`` filter removes some of the meta information stored +By default, the ``jpegoptim`` filter removes some meta information stored in the image. To remove all EXIF data and comments, set the ``strip_all`` option to ``true``: diff --git a/cookbook/assetic/yuicompressor.rst b/cookbook/assetic/yuicompressor.rst index 4d537c71c17..ee7474e00fe 100644 --- a/cookbook/assetic/yuicompressor.rst +++ b/cookbook/assetic/yuicompressor.rst @@ -18,7 +18,7 @@ Download the YUI Compressor JAR ------------------------------- The YUI Compressor is written in Java and distributed as a JAR. `Download the JAR`_ -from the Yahoo! site and save it to ``app/Resources/java/yuicompressor.jar``. +from the Yahoo! website and save it to ``app/Resources/java/yuicompressor.jar``. Configure the YUI Filters ------------------------- From 2ec4855b59b9cc222a4b6e4984e08142781d996c Mon Sep 17 00:00:00 2001 From: frne Date: Wed, 24 Dec 2014 09:11:20 +0100 Subject: [PATCH 0172/2667] Style / grammar fixes for the bundles chapter --- cookbook/bundles/configuration.rst | 6 +++--- cookbook/bundles/override.rst | 4 ++-- cookbook/bundles/prepend_extension.rst | 2 +- cookbook/bundles/remove.rst | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cookbook/bundles/configuration.rst b/cookbook/bundles/configuration.rst index 43c5242ed5c..6c2bfb20b20 100644 --- a/cookbook/bundles/configuration.rst +++ b/cookbook/bundles/configuration.rst @@ -210,7 +210,7 @@ The ``Configuration`` class to handle the sample configuration looks like:: supporting "prototype" nodes, advanced validation, XML-specific normalization and advanced merging. You can read more about this in :doc:`the Config component documentation `. You - can also see it in action by checking out some of the core Configuration + can also see it in action by checking out some core Configuration classes, such as the one from the `FrameworkBundle Configuration`_ or the `TwigBundle Configuration`_. @@ -227,7 +227,7 @@ thrown):: } The ``processConfiguration()`` method uses the configuration tree you've defined -in the ``Configuration`` class to validate, normalize and merge all of the +in the ``Configuration`` class to validate, normalize and merge all the configuration arrays together. .. tip:: @@ -253,7 +253,7 @@ configuration arrays together. } This class uses the ``getConfiguration()`` method to get the Configuration - instance, you should override it if your Configuration class is not called + instance. You should override it, if your Configuration class is not called ``Configuration`` or if it is not placed in the same namespace as the extension. diff --git a/cookbook/bundles/override.rst b/cookbook/bundles/override.rst index 70ba6786dde..dabade0b5eb 100644 --- a/cookbook/bundles/override.rst +++ b/cookbook/bundles/override.rst @@ -90,8 +90,8 @@ In this example you fetch the service definition of the original service, and se its class name to your own class. See :doc:`/cookbook/service_container/compiler_passes` for information on how to use -compiler passes. If you want to do something beyond just overriding the class - -like adding a method call - you can only use the compiler pass method. +compiler passes. If you want to do something beyond just overriding the class, +like adding a method call, you can only use the compiler pass method. Entities & Entity Mapping ------------------------- diff --git a/cookbook/bundles/prepend_extension.rst b/cookbook/bundles/prepend_extension.rst index 35deb3731b0..f4a0aff3cc9 100644 --- a/cookbook/bundles/prepend_extension.rst +++ b/cookbook/bundles/prepend_extension.rst @@ -15,7 +15,7 @@ often need to be repeated for various bundles. Using the below approach, it is possible to remove the disadvantage of the multiple bundle approach by enabling a single Extension to prepend the settings for any bundle. It can use the settings defined in the ``app/config/config.yml`` -to prepend settings just as if they would have been written explicitly by +to prepend settings just as if they had been written explicitly by the user in the application configuration. For example, this could be used to configure the entity manager name to use in diff --git a/cookbook/bundles/remove.rst b/cookbook/bundles/remove.rst index 407ee421aa4..687f4153539 100644 --- a/cookbook/bundles/remove.rst +++ b/cookbook/bundles/remove.rst @@ -56,7 +56,7 @@ Remove the ``_acme_demo`` entry at the bottom of this file. Some bundles contain configuration in one of the ``app/config/config*.yml`` files. Be sure to remove the related configuration from these files. You can -quickly spot bundle configuration by looking for a ``acme_demo`` (or whatever +quickly spot bundle configuration by looking for an ``acme_demo`` (or whatever the name of the bundle is, e.g. ``fos_user`` for the FOSUserBundle) string in the configuration files. From ba6f59f3fc4e87d4052cd20ff4dd2dd754f6bd2e Mon Sep 17 00:00:00 2001 From: frne Date: Wed, 24 Dec 2014 09:15:18 +0100 Subject: [PATCH 0173/2667] Style / grammar fixes for the cache chapter --- cookbook/cache/varnish.rst | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/cookbook/cache/varnish.rst b/cookbook/cache/varnish.rst index 054269d6393..7df7f710abd 100644 --- a/cookbook/cache/varnish.rst +++ b/cookbook/cache/varnish.rst @@ -29,17 +29,16 @@ in the Symfony configuration so that Varnish is seen as a trusted proxy and the Routing and X-FORWARDED Headers ------------------------------- -If the ``X-Forwarded-Port`` header is not set correctly, Symfony will append -the port where the PHP application is running when generating absolute URLs, -e.g. ``http://example.com:8080/my/path``. To ensure that the Symfony router -generates URLs correctly with Varnish, add the correct port number in the -``X-Forwarded-Port`` header. This port depends on your setup. - -Suppose that external connections come in on the default HTTP port 80. For HTTPS -connections, there is another proxy (as Varnish does not do HTTPS itself) on the -default HTTPS port 443 that handles the SSL termination and forwards the requests -as HTTP requests to Varnish with a ``X-Forwarded-Proto`` header. In this case, -add the following to your Varnish configuration: +To ensure that the Symfony Router generates URLs correctly with Varnish, +an ``X-Forwarded-Port`` header must be present for Symfony to use the +correct port number. + +This port number corresponds to the port your setup is using to receive external +connections (``80`` is the default value for HTTP connections). If the application +also accepts HTTPS connections, there could be another proxy (as Varnish does +not do HTTPS itself) on the default HTTPS port 443 that handles the SSL termination +and forwards the requests as HTTP requests to Varnish with an ``X-Forwarded-Proto`` +header. In this case, you need to add the following configuration snippet: .. code-block:: varnish4 @@ -192,7 +191,7 @@ Symfony adds automatically: .. tip:: If you followed the advice about ensuring a consistent caching - behavior, those vcl functions already exist. Just append the code + behaviour, those VCL functions already exist. Just append the code to the end of the function, they won't interfere with each other. .. index:: From 21ebbd421f85b27e52c27050453db22b16ab1af1 Mon Sep 17 00:00:00 2001 From: frne Date: Wed, 24 Dec 2014 09:38:30 +0100 Subject: [PATCH 0174/2667] Style / grammar fixes for the configuration chapter --- cookbook/configuration/apache_router.rst | 2 +- cookbook/configuration/environments.rst | 8 ++++---- cookbook/configuration/front_controllers_and_kernel.rst | 2 +- cookbook/configuration/pdo_session_storage.rst | 4 ++-- cookbook/configuration/using_parameters_in_dic.rst | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cookbook/configuration/apache_router.rst b/cookbook/configuration/apache_router.rst index f4bfbc7cdb6..99f1734f09b 100644 --- a/cookbook/configuration/apache_router.rst +++ b/cookbook/configuration/apache_router.rst @@ -133,7 +133,7 @@ You're now all set to use Apache routes. Additional Tweaks ----------------- -To save a little bit of processing time, change occurrences of ``Request`` +To save some processing time, change occurrences of ``Request`` to ``ApacheRequest`` in ``web/app.php``:: // web/app.php diff --git a/cookbook/configuration/environments.rst b/cookbook/configuration/environments.rst index e78c8577f8e..05919e7e769 100644 --- a/cookbook/configuration/environments.rst +++ b/cookbook/configuration/environments.rst @@ -6,7 +6,7 @@ How to Master and Create new Environments Every application is the combination of code and a set of configuration that dictates how that code should function. The configuration may define the -database being used, whether or not something should be cached, or how verbose +database being used, whether something should be cached, or how verbose logging should be. In Symfony, the idea of "environments" is the idea that the same codebase can be run using multiple different configurations. For example, the ``dev`` environment should use configuration that makes development @@ -170,10 +170,10 @@ this code and changing the environment string. Important, but unrelated to the topic of *environments* is the ``false`` argument as the second argument to the ``AppKernel`` constructor. This - specifies whether or not the application should run in "debug mode". Regardless + specifies if the application should run in "debug mode". Regardless of the environment, a Symfony application can be run with debug mode set to ``true`` or ``false``. This affects many things in the application, - such as whether or not errors should be displayed or if cache files are + such as if errors should be displayed or if cache files are dynamically rebuilt on each request. Though not a requirement, debug mode is generally set to ``true`` for the ``dev`` and ``test`` environments and ``false`` for the ``prod`` environment. @@ -322,7 +322,7 @@ The new environment is now accessible via:: .. note:: Some environments, like the ``dev`` environment, are never meant to be - accessed on any deployed server by the general public. This is because + accessed on any deployed server by the public. This is because certain environments, for debugging purposes, may give too much information about the application or underlying infrastructure. To be sure these environments aren't accessible, the front controller is usually protected from external diff --git a/cookbook/configuration/front_controllers_and_kernel.rst b/cookbook/configuration/front_controllers_and_kernel.rst index f1906c16837..b7d00a1bfd8 100644 --- a/cookbook/configuration/front_controllers_and_kernel.rst +++ b/cookbook/configuration/front_controllers_and_kernel.rst @@ -39,7 +39,7 @@ The main purpose of the front controller is to create an instance of the and return the resulting response to the browser. Because every request is routed through it, the front controller can be -used to perform global initializations prior to setting up the kernel or +used to perform global initialization prior to setting up the kernel or to `decorate`_ the kernel with additional features. Examples include: * Configuring the autoloader or adding additional autoloading mechanisms; diff --git a/cookbook/configuration/pdo_session_storage.rst b/cookbook/configuration/pdo_session_storage.rst index 1da69dfc332..03445fadbf1 100644 --- a/cookbook/configuration/pdo_session_storage.rst +++ b/cookbook/configuration/pdo_session_storage.rst @@ -7,7 +7,7 @@ How to Use PdoSessionHandler to Store Sessions in the Database The default Symfony session storage writes the session information to files. Most medium to large websites use a database to store the session values instead of files, because databases are easier to use and scale in a -multi-webserver environment. +multi webserver environment. Symfony has a built-in solution for database session storage called :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\PdoSessionHandler`. @@ -16,7 +16,7 @@ To use it, you just need to change some parameters in the main configuration fil .. versionadded:: 2.1 In Symfony 2.1 the class and namespace are slightly modified. You can now find the session storage classes in the ``Session\Storage`` namespace: - ``Symfony\Component\HttpFoundation\Session\Storage``. Also + ``Symfony\Component\HttpFoundation\Session\Storage``. Also, note that in Symfony 2.1 you should configure ``handler_id`` not ``storage_id`` like in Symfony 2.0. Below, you'll notice that ``%session.storage.options%`` is not used anymore. diff --git a/cookbook/configuration/using_parameters_in_dic.rst b/cookbook/configuration/using_parameters_in_dic.rst index f5c00244797..03bc453bba4 100644 --- a/cookbook/configuration/using_parameters_in_dic.rst +++ b/cookbook/configuration/using_parameters_in_dic.rst @@ -9,7 +9,7 @@ You have seen how to use configuration parameters within There are special cases such as when you want, for instance, to use the ``%kernel.debug%`` parameter to make the services in your bundle enter debug mode. For this case there is more work to do in order -to make the system understand the parameter value. By default +to make the system understand the parameter value. By default, your parameter ``%kernel.debug%`` will be treated as a simple string. Consider the following example:: From 974e17fccc6579a3ab22e05f235c6a4e39c47e7d Mon Sep 17 00:00:00 2001 From: Frank Neff Date: Sun, 28 Dec 2014 13:54:06 +0100 Subject: [PATCH 0175/2667] Rewritten 'whether' & removed serial comma --- cookbook/configuration/environments.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/configuration/environments.rst b/cookbook/configuration/environments.rst index 05919e7e769..722be814edf 100644 --- a/cookbook/configuration/environments.rst +++ b/cookbook/configuration/environments.rst @@ -6,7 +6,7 @@ How to Master and Create new Environments Every application is the combination of code and a set of configuration that dictates how that code should function. The configuration may define the -database being used, whether something should be cached, or how verbose +database being used, if something should be cached or how verbose logging should be. In Symfony, the idea of "environments" is the idea that the same codebase can be run using multiple different configurations. For example, the ``dev`` environment should use configuration that makes development From a0abc5d3435f2b27c9801dce39e4570c3c2af561 Mon Sep 17 00:00:00 2001 From: Frank Neff Date: Sun, 28 Dec 2014 13:59:14 +0100 Subject: [PATCH 0176/2667] Removes wrong comma --- cookbook/bundles/configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/bundles/configuration.rst b/cookbook/bundles/configuration.rst index 6c2bfb20b20..f04f1018ccf 100644 --- a/cookbook/bundles/configuration.rst +++ b/cookbook/bundles/configuration.rst @@ -253,7 +253,7 @@ configuration arrays together. } This class uses the ``getConfiguration()`` method to get the Configuration - instance. You should override it, if your Configuration class is not called + instance. You should override it if your Configuration class is not called ``Configuration`` or if it is not placed in the same namespace as the extension. From 136230fca753bed1d7275bf7f41ad2b45cae176f Mon Sep 17 00:00:00 2001 From: frne Date: Sun, 28 Dec 2014 16:09:39 +0100 Subject: [PATCH 0177/2667] Style / grammar fixes for the console chapter --- cookbook/console/console_command.rst | 2 +- cookbook/console/usage.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/console/console_command.rst b/cookbook/console/console_command.rst index 028e85f423f..7cbaba616d1 100644 --- a/cookbook/console/console_command.rst +++ b/cookbook/console/console_command.rst @@ -147,7 +147,7 @@ before translating contents:: } } -However for other services the solution might be more complex. For more details, +However, for other services the solution might be more complex. For more details, see :doc:`/cookbook/service_container/scopes`. Testing Commands diff --git a/cookbook/console/usage.rst b/cookbook/console/usage.rst index dc3495261c9..6275f760aa0 100644 --- a/cookbook/console/usage.rst +++ b/cookbook/console/usage.rst @@ -56,7 +56,7 @@ When using the shell you can choose to run each command in a separate process: $ php app/console -s --process-isolation When you do this, the output will not be colorized and interactivity is not -supported so you will need to pass all command params explicitly. +supported so you will need to pass all command parameters explicitly. .. note:: From c56f9126344cd2fe1ecac31270de405e459b772e Mon Sep 17 00:00:00 2001 From: frne Date: Sun, 28 Dec 2014 16:13:49 +0100 Subject: [PATCH 0178/2667] Style / grammar fixes for the controller chapter --- cookbook/controller/service.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/controller/service.rst b/cookbook/controller/service.rst index 4fa3af4deb5..7b077c0f0e0 100644 --- a/cookbook/controller/service.rst +++ b/cookbook/controller/service.rst @@ -11,11 +11,11 @@ this works fine, controllers can also be specified as services. .. note:: - Specifying a controller as a service takes a little bit more work. The + Specifying a controller as a service takes a bit more work. The primary advantage is that the entire controller or any services passed to the controller can be modified via the service container configuration. This is especially useful when developing an open-source bundle or any - bundle that will be used in many different projects. + bundle that will be used in different projects. A second advantage is that your controllers are more "sandboxed". By looking at the constructor arguments, it's easy to see what types of things From 2af84eb33f1a4f31ca0de7d239f036b0eca416d9 Mon Sep 17 00:00:00 2001 From: frne Date: Sun, 28 Dec 2014 18:31:08 +0100 Subject: [PATCH 0179/2667] Style / grammar fixes for the deployment chapter --- cookbook/deployment/azure-website.rst | 22 +++++++-------- cookbook/deployment/heroku.rst | 6 ++--- cookbook/deployment/tools.rst | 39 +++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 14 deletions(-) diff --git a/cookbook/deployment/azure-website.rst b/cookbook/deployment/azure-website.rst index efee282260e..c351fb8f390 100644 --- a/cookbook/deployment/azure-website.rst +++ b/cookbook/deployment/azure-website.rst @@ -6,14 +6,14 @@ Deploying to Microsoft Azure Website Cloud This step by step cookbook describes how to deploy a small Symfony web application to the Microsoft Azure Website cloud platform. It will explain how -to setup a new Azure website including configuring the right PHP version and +to set up a new Azure website including configuring the right PHP version and global environment variables. The document also shows how to you can leverage Git and Composer to deploy your Symfony application to the cloud. Setting up the Azure Website ---------------------------- -To setup a new Microsoft Azure Website, first `signup with Azure`_ or sign in +To set up a new Microsoft Azure Website, first `sign up with Azure`_ or sign in with your credentials. Once you're connected to your `Azure Portal`_ interface, scroll down to the bottom and select the **New** panel. On this panel, click **Web Site** and choose **Custom Create**: @@ -41,7 +41,7 @@ next step. Step 2: New MySQL Database ~~~~~~~~~~~~~~~~~~~~~~~~~~ -On this step, you will be prompted to setup your MySQL database storage with a +On this step, you will be prompted to set up your MySQL database storage with a database name and a region. The MySQL database storage is provided by Microsoft in partnership with ClearDB. Choose the same region you selected for the hosting plan configuration in the previous step. @@ -114,7 +114,7 @@ the web server. Choosing a more recent PHP version can greatly improve runtime performance. PHP 5.5 ships with a new built-in PHP accelerator called OPCache that replaces APC. On an Azure Website, OPCache is already enabled and there - is no need to install and setup APC. + is no need to install and set up APC. The following screenshot shows the output of a :phpfunction:`phpinfo` script run from an Azure Website to verify that PHP 5.5 is running with @@ -171,7 +171,7 @@ this file just needs to be moved into the custom website extension directory. longer be necessary. To get the ``php_intl.dll`` file under your ``site/wwwroot`` directory, simply -access the online **Kudu** tool by browsing to the following url: +access the online **Kudu** tool by browsing to the following URL: .. code-block:: text @@ -342,7 +342,7 @@ credentials, CSRF token protection, etc. These parameters come from the .. image:: /images/cookbook/deployment/azure-website/step-16.png :alt: Configuring Symfony global parameters -The most important thing in this cookbook is to correctly setup your database +The most important thing in this cookbook is to correctly set up your database settings. You can get your MySQL database settings on the right sidebar of the **Azure Website Dashboard** panel. Simply click on the **View Connection Strings** link to make them appear in a pop-in. @@ -395,7 +395,7 @@ Symfony application is more complex than a basic Symfony Standard Edition, you may have additional commands to execute for setup (see :doc:`/cookbook/deployment/tools`). Make sure that your application is running by browsing the ``app.php`` front -controller with your web browser and the following url: +controller with your web browser and the following URL: .. code-block:: bash @@ -408,7 +408,7 @@ Configure the Web Server ~~~~~~~~~~~~~~~~~~~~~~~~ At this point, the Symfony application has been deployed and works perfectly on -the Azure Website. However, the ``web`` folder is still part of the url, which +the Azure Website. However, the ``web`` folder is still part of the URL, which you definitely don't want. But don't worry! You can easily configure the web server to point to the ``web`` folder and remove the ``web`` in the URL (and guarantee that nobody can access files outside of the ``web`` directory.) @@ -453,9 +453,9 @@ application, configure it with the following content: As you can see, the latest rule ``RewriteRequestsToPublic`` is responsible for -rewriting any urls to the ``web/app.php`` front controller which allows you to +rewriting any URLs to the ``web/app.php`` front controller which allows you to skip the ``web/`` folder in the URL. The first rule called ``BlockAccessToPublic`` -matches all url patterns that contain the ``web/`` folder and serves a +matches all URL patterns that contain the ``web/`` folder and serves a ``403 Forbidden`` HTTP response instead. This example is based on Benjamin Eberlei's sample you can find on GitHub in the `SymfonyAzureEdition`_ bundle. @@ -471,7 +471,7 @@ and executed on a Microsoft IIS web server. The process is simple and easy to implement. And as a bonus, Microsoft is continuing to reduce the number of steps needed so that deployment becomes even easier. -.. _`signup with Azure`: https://signup.live.com/signup.aspx +.. _`sign up with Azure`: https://signup.live.com/signup.aspx .. _`Azure Portal`: https://manage.windowsazure.com .. _`PHP MSDN documentation`: http://blogs.msdn.com/b/silverlining/archive/2012/07/10/configuring-php-in-windows-azure-websites-with-user-ini-files.aspx .. _`git-scm.com`: http://git-scm.com/download diff --git a/cookbook/deployment/heroku.rst b/cookbook/deployment/heroku.rst index 57e8c89666e..7ed6216eef9 100644 --- a/cookbook/deployment/heroku.rst +++ b/cookbook/deployment/heroku.rst @@ -11,7 +11,7 @@ published by Heroku. Setting up ---------- -To setup a new Heroku website, first `signup with Heroku`_ or sign in +To set up a new Heroku website, first `sign up with Heroku`_ or sign in with your credentials. Then download and install the `Heroku Toolbelt`_ on your local computer. @@ -133,7 +133,7 @@ create the ``Procfile`` file and to add it to the repository: 2) Set the Environment to prod ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -During a deploy, Heroku runs ``composer install --no-dev`` to install all of the +During a deployment, Heroku runs ``composer install --no-dev`` to install all the dependencies your application requires. However, typical `post-install-commands`_ in ``composer.json``, e.g. to install assets or clear (or pre-warm) caches, run using Symfony's ``dev`` environment by default. @@ -325,7 +325,7 @@ This is also very useful to build assets on the production system, e.g. with Ass `Grunt`_ or `gulp`_. .. _`the original article`: https://devcenter.heroku.com/articles/getting-started-with-symfony2 -.. _`signup with Heroku`: https://signup.heroku.com/signup/dc +.. _`sign up with Heroku`: https://signup.heroku.com/signup/dc .. _`Heroku Toolbelt`: https://devcenter.heroku.com/articles/getting-started-with-php#local-workstation-setup .. _`getting Started with PHP on Heroku`: https://devcenter.heroku.com/articles/getting-started-with-php .. _`ephemeral file system`: https://devcenter.heroku.com/articles/dynos#ephemeral-filesystem diff --git a/cookbook/deployment/tools.rst b/cookbook/deployment/tools.rst index 322154ba9f2..e32832d8d25 100644 --- a/cookbook/deployment/tools.rst +++ b/cookbook/deployment/tools.rst @@ -189,6 +189,45 @@ Don't forget that deploying your application also involves updating any dependen (typically via Composer), migrating your database, clearing your cache and other potential things like pushing assets to a CDN (see `Common Post-Deployment Tasks`_). +The Tools +--------- + +`Capifony`_: + + This tool provides a specialized set of tools on top of Capistrano, tailored + specifically to symfony and Symfony projects. + +`sf2debpkg`_: + + This tool helps you build a native Debian package for your Symfony project. + +`Magallanes`_: + + This Capistrano-like deployment tool is built in PHP, and may be easier + for PHP developers to extend for their needs. + +Bundles: + + There are many `bundles that add deployment features`_ directly into your + Symfony console. + +Basic scripting: + + You can of course use shell, `Ant`_, or any other build tool to script + the deploying of your project. + +Platform as a Service Providers: + + PaaS is a relatively new way to deploy your application. Typically, a PaaS + will use a single configuration file in your project's root directory to + determine how to build an environment on the fly that supports your software. + One provider with confirmed Symfony support is `PagodaBox`_. + +.. tip:: + + Looking for more? Talk to the community on the `Symfony IRC channel`_ #symfony + (on freenode) for more information. + .. _`Capifony`: http://capifony.org/ .. _`Capistrano`: http://capistranorb.com/ .. _`sf2debpkg`: https://github.com/liip/sf2debpkg From a75cc152bdcc4fc3b443d4f7c2c9357ef9397893 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 23 Jun 2015 15:57:00 +0200 Subject: [PATCH 0180/2667] Removed some information wrongly rebased --- cookbook/deployment/tools.rst | 39 ----------------------------------- 1 file changed, 39 deletions(-) diff --git a/cookbook/deployment/tools.rst b/cookbook/deployment/tools.rst index e32832d8d25..322154ba9f2 100644 --- a/cookbook/deployment/tools.rst +++ b/cookbook/deployment/tools.rst @@ -189,45 +189,6 @@ Don't forget that deploying your application also involves updating any dependen (typically via Composer), migrating your database, clearing your cache and other potential things like pushing assets to a CDN (see `Common Post-Deployment Tasks`_). -The Tools ---------- - -`Capifony`_: - - This tool provides a specialized set of tools on top of Capistrano, tailored - specifically to symfony and Symfony projects. - -`sf2debpkg`_: - - This tool helps you build a native Debian package for your Symfony project. - -`Magallanes`_: - - This Capistrano-like deployment tool is built in PHP, and may be easier - for PHP developers to extend for their needs. - -Bundles: - - There are many `bundles that add deployment features`_ directly into your - Symfony console. - -Basic scripting: - - You can of course use shell, `Ant`_, or any other build tool to script - the deploying of your project. - -Platform as a Service Providers: - - PaaS is a relatively new way to deploy your application. Typically, a PaaS - will use a single configuration file in your project's root directory to - determine how to build an environment on the fly that supports your software. - One provider with confirmed Symfony support is `PagodaBox`_. - -.. tip:: - - Looking for more? Talk to the community on the `Symfony IRC channel`_ #symfony - (on freenode) for more information. - .. _`Capifony`: http://capifony.org/ .. _`Capistrano`: http://capistranorb.com/ .. _`sf2debpkg`: https://github.com/liip/sf2debpkg From a3915632424d5f932a9c6ef54cc35d05ed930e88 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 19 Feb 2015 10:25:31 +0100 Subject: [PATCH 0181/2667] Updated the best practices article for reusable bundles --- cookbook/bundles/best_practices.rst | 343 +++++++++++++--------------- 1 file changed, 160 insertions(+), 183 deletions(-) diff --git a/cookbook/bundles/best_practices.rst b/cookbook/bundles/best_practices.rst index 4a9468b11cd..7f410200831 100644 --- a/cookbook/bundles/best_practices.rst +++ b/cookbook/bundles/best_practices.rst @@ -13,8 +13,12 @@ This article is all about how to structure your **reusable bundles** so that they're easy to configure and extend. Many of these recommendations do not apply to application bundles because you'll want to keep those as simple as possible. For application bundles, just follow the practices shown throughout -the :doc:`book `, the :doc:`cookbook ` and the -:doc:`best practices ` book. +the book and cookbook. + +.. seealso:: + + The best practices for application-specific bundles are discussed in + :doc:`/best_practices/introduction`. .. index:: pair: Bundle; Naming conventions @@ -24,8 +28,8 @@ the :doc:`book `, the :doc:`cookbook ` and the Bundle Name ----------- -A bundle is also a PHP namespace. The namespace must follow the technical -interoperability `standards`_ for PHP namespaces and class names: it starts +A bundle is also a PHP namespace. The namespace must follow the `PSR-0`_ or +`PSR-4`_ interoperability standards for PHP namespaces and class names: it starts with a vendor segment, followed by zero or more category segments, and it ends with the namespace short name, which must end with a ``Bundle`` suffix. @@ -41,15 +45,12 @@ bundle class name must follow these simple rules: Here are some valid bundle namespaces and class names: -+-----------------------------------+--------------------------+ -| Namespace | Bundle Class Name | -+===================================+==========================+ -| ``Acme\Bundle\BlogBundle`` | ``AcmeBlogBundle`` | -+-----------------------------------+--------------------------+ -| ``Acme\Bundle\Social\BlogBundle`` | ``AcmeSocialBlogBundle`` | -+-----------------------------------+--------------------------+ -| ``Acme\BlogBundle`` | ``AcmeBlogBundle`` | -+-----------------------------------+--------------------------+ +========================== ================== +Namespace Bundle Class Name +========================== ================== +``Acme\Bundle\BlogBundle`` ``AcmeBlogBundle`` +``Acme\BlogBundle`` ``AcmeBlogBundle`` +========================== ================== By convention, the ``getName()`` method of the bundle class should return the class name. @@ -67,49 +68,47 @@ class name. :class:`Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle`. Each bundle has an alias, which is the lower-cased short version of the bundle -name using underscores (``acme_hello`` for ``AcmeHelloBundle``, or -``acme_social_blog`` for ``Acme\Social\BlogBundle`` for instance). This alias -is used to enforce uniqueness within a bundle (see below for some usage -examples). +name using underscores (``acme_blog`` for ``AcmeBlogBundle``). This alias +is used to enforce uniqueness within a project and for defining bundle's +configuration options (see below for some usage examples). Directory Structure ------------------- -The basic directory structure of a HelloBundle must read as follows: +The basic directory structure of an AcmeBlogBundle must read as follows: .. code-block:: text - XXX/... - HelloBundle/ - HelloBundle.php - Controller/ - Resources/ - meta/ - LICENSE - config/ - doc/ - index.rst - translations/ - views/ - public/ - Tests/ - -The ``XXX`` directory(ies) reflects the namespace structure of the bundle. - -The following files are mandatory: - -* ``HelloBundle.php``; -* ``Resources/meta/LICENSE``: The full license for the code; + / + ├─ AcmeBlogBundle.php + ├─ Controller/ + ├─ README.md + ├─ Resources/ + │ ├─ meta/ + │ │ └─ LICENSE + │ ├─ config/ + │ ├─ doc/ + │ │ └─ index.rst + │ ├─ translations/ + │ ├─ views/ + │ └─ public/ + └─ Tests/ + +**The following files are mandatory**, because they ensure a structure convention +that automated tools can rely on: + +* ``AcmeBlogBundle.php``: This is the class that transforms a plain directory + into a Symfony bundle; +* ``README.md``: This file contains the basic description of the bundle and it + usually shows some basic examples and links to its full documentation (it + can use any of the markup formats supported by GitHub, such as ``README.rst``); +* ``Resources/meta/LICENSE``: The full license for the code. The license file + can also be stored in the bundle's root directory to follow the generic + conventions about packages; * ``Resources/doc/index.rst``: The root file for the Bundle documentation. -.. note:: - - These conventions ensure that automated tools can rely on this default - structure to work. - -The depth of sub-directories should be kept to the minimal for most used -classes and files (two levels at a maximum). More levels can be defined for -non-strategic, less-used files. +The depth of sub-directories should be kept to the minimum for most used +classes and files (two levels maximum). The bundle directory is read-only. If you need to write temporary files, store them under the ``cache/`` or ``log/`` directory of the host application. Tools @@ -118,41 +117,31 @@ files are going to be part of the repository. The following classes and files have specific emplacements: -+------------------------------+-----------------------------+ -| Type | Directory | -+==============================+=============================+ -| Commands | ``Command/`` | -+------------------------------+-----------------------------+ -| Controllers | ``Controller/`` | -+------------------------------+-----------------------------+ -| Service Container Extensions | ``DependencyInjection/`` | -+------------------------------+-----------------------------+ -| Event Listeners | ``EventListener/`` | -+------------------------------+-----------------------------+ -| Configuration | ``Resources/config/`` | -+------------------------------+-----------------------------+ -| Web Resources | ``Resources/public/`` | -+------------------------------+-----------------------------+ -| Translation files | ``Resources/translations/`` | -+------------------------------+-----------------------------+ -| Templates | ``Resources/views/`` | -+------------------------------+-----------------------------+ -| Unit and Functional Tests | ``Tests/`` | -+------------------------------+-----------------------------+ - -.. note:: - - When building a reusable bundle, model classes should be placed in the - ``Model`` namespace. See :doc:`/cookbook/doctrine/mapping_model_classes` for - how to handle the mapping with a compiler pass. +=============================== ============================= +Type Directory +=============================== ============================= +Commands ``Command/`` +Controllers ``Controller/`` +Service Container Extensions ``DependencyInjection/`` +Event Listeners ``EventListener/`` +Model classes [1] ``Model/`` +Configuration ``Resources/config/`` +Web Resources (CSS, JS, images) ``Resources/public/`` +Translation files ``Resources/translations/`` +Templates ``Resources/views/`` +Unit and Functional Tests ``Tests/`` +=============================== ============================= + +[1] See :doc:`/cookbook/doctrine/mapping_model_classes` for how to handle the +mapping with a compiler pass. Classes ------- The bundle directory structure is used as the namespace hierarchy. For -instance, a ``HelloController`` controller is stored in -``Bundle/HelloBundle/Controller/HelloController.php`` and the fully qualified -class name is ``Bundle\HelloBundle\Controller\HelloController``. +instance, a ``ContentController`` controller is stored in +``Acme/BlogBundle/Controller/ContentController.php`` and the fully qualified +class name is ``Acme\BlogBundle\Controller\ContentController``. All classes and files must follow the :doc:`Symfony coding standards
`. @@ -162,7 +151,7 @@ Commands, Helpers, Listeners, and Controllers. Classes that connect to the event dispatcher should be suffixed with ``Listener``. -Exception classes should be stored in an ``Exception`` sub-namespace. +Exceptions classes should be stored in an ``Exception`` sub-namespace. Vendors ------- @@ -177,7 +166,7 @@ Tests ----- A bundle should come with a test suite written with PHPUnit and stored under -the ``Tests/`` directory. Tests should follow these principles: +the ``Tests/`` directory. Tests should follow the following principles: * The test suite must be executable with a simple ``phpunit`` command run from a sample application; @@ -186,14 +175,13 @@ the ``Tests/`` directory. Tests should follow these principles: * The tests should cover at least 95% of the code base. .. note:: - A test suite must not contain ``AllTests.php`` scripts, but must rely on the existence of a ``phpunit.xml.dist`` file. Documentation ------------- -All classes and functions must be fully documented using `PHPDoc`_ tags. +All classes and functions must come with full PHPDoc. Extensive documentation should also be provided in the :doc:`reStructuredText ` format, under @@ -206,108 +194,55 @@ Installation Instructions In order to ease the installation of third-party bundles, consider using the following standardized instructions in your ``README.md`` file. -.. configuration-block:: - - .. code-block:: markdown +.. code-block:: text - Installation - ============ + Installation + ============ - Step 1: Download the Bundle - --------------------------- + Step 1: Download the Bundle + --------------------------- - Open a command console, enter your project directory and execute the - following command to download the latest stable version of this bundle: + Open a command console, enter your project directory and execute the + following command to download the latest stable version of this bundle: - ```bash - $ composer require "~1" - ``` + ```bash + $ composer require "~1" + ``` - This command requires you to have Composer installed globally, as explained - in the [installation chapter](https://getcomposer.org/doc/00-intro.md) - of the Composer documentation. + This command requires you to have Composer installed globally, as explained + in the [installation chapter](https://getcomposer.org/doc/00-intro.md) + of the Composer documentation. - Step 2: Enable the Bundle - ------------------------- + Step 2: Enable the Bundle + ------------------------- - Then, enable the bundle by adding it to the list of registered bundles - in the `app/AppKernel.php` file of your project: + Then, enable the bundle by adding the following in the `app/AppKernel.php` + file of your project: - ```php - \\(), - ); - + $bundles = array( // ... - } - - // ... - } - ``` - - .. code-block:: rst - - Installation - ============ - - Step 1: Download the Bundle - --------------------------- - Open a command console, enter your project directory and execute the - following command to download the latest stable version of this bundle: - - .. code-block:: bash - - $ composer require "~1" - - This command requires you to have Composer installed globally, as explained - in the `installation chapter`_ of the Composer documentation. - - Step 2: Enable the Bundle - ------------------------- - - Then, enable the bundle by adding it to the list of registered bundles - in the ``app/AppKernel.php`` file of your project: - - .. code-block:: php - - \\(), + ); // ... - class AppKernel extends Kernel - { - public function registerBundles() - { - $bundles = array( - // ... - - new \\(), - ); - - // ... - } - - // ... - } + } - .. _`installation chapter`: https://getcomposer.org/doc/00-intro.md + // ... + } + ``` -The example above assumes that you are installing the latest stable version of -the bundle, where you don't have to provide the package version number -(e.g. ``composer require friendsofsymfony/user-bundle``). If the installation -instructions refer to some past bundle version or to some unstable version, -include the version constraint (e.g. ``composer require friendsofsymfony/user-bundle "~2.0@dev"``). +This template assumes that your bundle is in its ``1.x`` version. If not, change +the ``"~1"`` installation version accordingly (``"~2"``, ``"~3"``, etc.) Optionally, you can add more installation steps (*Step 3*, *Step 4*, etc.) to explain other required installation tasks, such as registering routes or @@ -317,8 +252,8 @@ Routing ------- If the bundle provides routes, they must be prefixed with the bundle alias. -For an AcmeBlogBundle for instance, all routes must be prefixed with -``acme_blog_``. +For example, if your bundle is called AcmeBlogBundle, all its routes must be +prefixed with ``acme_blog_``. Templates --------- @@ -330,8 +265,7 @@ Translation Files ----------------- If a bundle provides message translations, they must be defined in the XLIFF -format; the :ref:`translation domain ` should be named -after the bundle name (``bundle.hello``). +format; the domain should be named after the bundle name (``acme_blog``). A bundle must not override existing messages from another bundle. @@ -346,7 +280,7 @@ the Symfony configuration. Symfony parameters are simple key/value pairs; a value being any valid PHP value. Each parameter name should start with the bundle alias, though this is just a best-practice suggestion. The rest of the parameter name will use a period (``.``) to separate different parts (e.g. -``acme_hello.email.from``). +``acme_blog.author.email``). The end user can provide values in any configuration file: @@ -356,31 +290,74 @@ The end user can provide values in any configuration file: # app/config/config.yml parameters: - acme_hello.email.from: fabien@example.com + acme_blog.author.email: fabien@example.com .. code-block:: xml - fabien@example.com + fabien@example.com .. code-block:: php // app/config/config.php - $container->setParameter('acme_hello.email.from', 'fabien@example.com'); + $container->setParameter('acme_blog.author.email', 'fabien@example.com'); Retrieve the configuration parameters in your code from the container:: - $container->getParameter('acme_hello.email.from'); + $container->getParameter('acme_blog.author.email'); -Even if this mechanism is simple enough, you are highly encouraged to use the -:doc:`semantic bundle configuration ` instead. +Even if this mechanism is simple enough, you should consider using the more +advanced :doc:`semantic bundle configuration `. -.. note:: +Versioning +---------- + +Bundles must be versioned following the `Semantic Versioning Standard`_. + +Services +-------- - If you are defining services, they should also be prefixed with the bundle - alias. +If the bundle defines services, they must be prefixed with the bundle alias. +For example, AcmeBlogBundle services must be prefixed with ``acme_blog``. + +In addition, services not meant to be used by the application directly, should +be :ref:`defined as private `. + +.. seealso:: + + You can learn much more about service loading in bundles reading this article: + :doc:`How to Load Service Configuration inside a Bundle `. + +Composer Metadata +----------------- -.. _`standards`: http://www.php-fig.org/psr/psr-0/ -.. _`PHPDoc`: https://en.wikipedia.org/wiki/PHPDoc +The ``composer.json`` file should include at least the following metadata: + +* ``name``, which includes the vendor and the short bundle name. If you are + releasing the bundle on your own instead of on behalf of a company, use your + personal name (e.g. ``johnsmith/blog-bundle``). The bundle short name excludes + the vendor name and separates each word with an hyphen. For example: + ``AcmeBlogBundle`` is transformed into ``blog-bundle`` and ``AcmeSocialConnectBundle`` + is transformed into ``social-connect-bundle``; +* ``description``, a brief explanation of the purpose of the bundle; +* ``type``, use the ``symfony-bundle`` value; +* ``license``, ``MIT`` is the preferred license for Symfony bundles, but you + can use any other value. +* ``autoload``, this information is used by Symfony to load the classes of the +bundle. The `PSR-4`_ autoload standard is recommended for modern bundles, but +`PSR-0`_ standard is also supported. + +In order to make it easier for developers to find your bundle, register it on +`Packagist`_, the official repository for Composer packages. + +Learn more from the Cookbook +---------------------------- + +* :doc:`/cookbook/bundles/extension` + +.. _`PSR-0`: http://www.php-fig.org/psr/psr-0/ +.. _`PSR-4`: http://www.php-fig.org/psr/psr-4/ +.. _`Semantic Versioning Standard`: http://semver.org/ +.. _`Packagist`: https://packagist.org/ From 9981193333c3f257f6affb03923d5f40397abece Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 19 Feb 2015 17:50:32 +0100 Subject: [PATCH 0182/2667] Minor fixes and added a cross link --- components/dependency_injection/advanced.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/dependency_injection/advanced.rst b/components/dependency_injection/advanced.rst index 0ea6b296a67..5e9b09d5ae0 100644 --- a/components/dependency_injection/advanced.rst +++ b/components/dependency_injection/advanced.rst @@ -4,6 +4,8 @@ Advanced Container Configuration ================================ +.. _container-private-services: + Marking Services as public / private ------------------------------------ From 381720b77da4eb7d4895885cea5d5bda40b34c9b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 23 Jun 2015 17:19:36 +0200 Subject: [PATCH 0183/2667] Completed the explanation as suggested by Ryan --- reference/forms/types/choice.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index 22b69947a4e..a275f77264a 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -132,6 +132,10 @@ The ``status`` field created by the code above will be rendered as: +But don't be confused! If ``Full`` is selected (value ``0`` in HTML), ``1`` will +be returned in your form. If ``Almost empty`` is selected (value ``2`` in HTML), +``0.1`` will be returned. + .. include:: /reference/forms/types/options/empty_value.rst.inc .. include:: /reference/forms/types/options/expanded.rst.inc From beac11e9d28b50b2d550a4f74ad698296c6daf4a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 23 Jun 2015 17:25:46 +0200 Subject: [PATCH 0184/2667] Use the built-in serializer instead of promoting JMS --- cookbook/serializer.rst | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/cookbook/serializer.rst b/cookbook/serializer.rst index 1f578cd3738..8d77c322701 100644 --- a/cookbook/serializer.rst +++ b/cookbook/serializer.rst @@ -6,13 +6,11 @@ How to Use the Serializer Serializing and deserializing to and from objects and different formats (e.g. JSON or XML) is a very complex topic. Symfony comes with a -:doc:`Serializer Component`, which gives you some +:doc:`Serializer Component `, which gives you some tools that you can leverage for your solution. In fact, before you start, get familiar with the serializer, normalizers -and encoders by reading the :doc:`Serializer Component`. -You should also check out the `JMSSerializerBundle`_, which expands on the -functionality offered by Symfony's core serializer. +and encoders by reading the :doc:`Serializer Component `. Activating the Serializer ------------------------- @@ -56,15 +54,15 @@ Adding Normalizers and Encoders ------------------------------- Once enabled, the ``serializer`` service will be available in the container -and will be loaded with two :ref:`encoders` +and will be loaded with two :ref:`encoders ` (:class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder` and :class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder`) -but no :ref:`normalizers`, meaning you'll +but no :ref:`normalizers `, meaning you'll need to load your own. You can load normalizers and/or encoders by tagging them as -:ref:`serializer.normalizer` and -:ref:`serializer.encoder`. It's also +:ref:`serializer.normalizer ` and +:ref:`serializer.encoder `. It's also possible to set the priority of the tag in order to decide the matching order. Here is an example on how to load the @@ -100,5 +98,3 @@ Here is an example on how to load the )); $definition->addTag('serializer.normalizer'); $container->setDefinition('get_set_method_normalizer', $definition); - -.. _JMSSerializerBundle: http://jmsyst.com/bundles/JMSSerializerBundle From 8e8fad26bf71f4cfd6c46261a2ed22ce43d454f8 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 23 Jun 2015 17:51:29 +0200 Subject: [PATCH 0185/2667] Removed some wrong labels --- cookbook/install/upgrading.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cookbook/install/upgrading.rst b/cookbook/install/upgrading.rst index 88a5ecc22a6..34d11369ce5 100644 --- a/cookbook/install/upgrading.rst +++ b/cookbook/install/upgrading.rst @@ -57,8 +57,6 @@ There are two steps to upgrading: :ref:`upgrade-minor-symfony-composer`; :ref:`upgrade-minor-symfony-code` -.. _`upgrade-minor-symfony-composer`: - 1) Update the Symfony Library via Composer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -96,8 +94,6 @@ But beware. If you have some bad `version constraints`_ in your ``composer.json` (e.g. ``dev-master``), then this could upgrade some non-Symfony libraries to new versions that contain backwards-compatibility breaking changes. -.. _`upgrade-minor-symfony-code`: - 2) Updating Your Code to Work with the new Version ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 797aab7 F987 adb61d785631c863dcddba963b750669e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 24 Jun 2015 08:53:07 +0200 Subject: [PATCH 0186/2667] Fixed minor issues --- cookbook/configuration/environments.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cookbook/configuration/environments.rst b/cookbook/configuration/environments.rst index 722be814edf..4f40b9d3d2d 100644 --- a/cookbook/configuration/environments.rst +++ b/cookbook/configuration/environments.rst @@ -5,13 +5,13 @@ How to Master and Create new Environments ========================================= Every application is the combination of code and a set of configuration that -dictates how that code should function. The configuration may define the -database being used, if something should be cached or how verbose -logging should be. In Symfony, the idea of "environments" is the idea that -the same codebase can be run using multiple different configurations. For -example, the ``dev`` environment should use configuration that makes development -easy and friendly, while the ``prod`` environment should use a set of configuration -optimized for speed. +dictates how that code should function. The configuration may define the database +being used, if something should be cached or how verbose logging should be. + +In Symfony, the idea of "environments" is the idea that the same codebase can be +run using multiple different configurations. For example, the ``dev`` environment +should use configuration that makes development easy and friendly, while the +``prod`` environment should use a set of configuration optimized for speed. .. index:: single: Environments; Configuration files From fa0b9029fa3d5a2f6cd2707fb83a307112a57750 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 24 Jun 2015 09:00:57 +0200 Subject: [PATCH 0187/2667] Fixed a minor syntax issue --- cookbook/bundles/best_practices.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/bundles/best_practices.rst b/cookbook/bundles/best_practices.rst index 7f410200831..88f9f25653d 100644 --- a/cookbook/bundles/best_practices.rst +++ b/cookbook/bundles/best_practices.rst @@ -346,8 +346,8 @@ The ``composer.json`` file should include at least the following metadata: * ``license``, ``MIT`` is the preferred license for Symfony bundles, but you can use any other value. * ``autoload``, this information is used by Symfony to load the classes of the -bundle. The `PSR-4`_ autoload standard is recommended for modern bundles, but -`PSR-0`_ standard is also supported. + bundle. The `PSR-4`_ autoload standard is recommended for modern bundles, but + `PSR-0`_ standard is also supported. In order to make it easier for developers to find your bundle, register it on `Packagist`_, the official repository for Composer packages. From 4276ced28474d7b56dfc169d51fbf93dfbdea873 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 24 Jun 2015 09:03:37 +0200 Subject: [PATCH 0188/2667] Added back some information wrongly removed during the rebase --- cookbook/bundles/best_practices.rst | 119 ++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 33 deletions(-) diff --git a/cookbook/bundles/best_practices.rst b/cookbook/bundles/best_practices.rst index 88f9f25653d..81e3d688948 100644 --- a/cookbook/bundles/best_practices.rst +++ b/cookbook/bundles/best_practices.rst @@ -194,55 +194,108 @@ Installation Instructions In order to ease the installation of third-party bundles, consider using the following standardized instructions in your ``README.md`` file. -.. code-block:: text +.. configuration-block:: - Installation - ============ + .. code-block:: markdown - Step 1: Download the Bundle - --------------------------- + Installation + ============ - Open a command console, enter your project directory and execute the - following command to download the latest stable version of this bundle: + Step 1: Download the Bundle + --------------------------- - ```bash - $ composer require "~1" - ``` + Open a command console, enter your project directory and execute the + following command to download the latest stable version of this bundle: - This command requires you to have Composer installed globally, as explained - in the [installation chapter](https://getcomposer.org/doc/00-intro.md) - of the Composer documentation. + ```bash + $ composer require "~1" + ``` - Step 2: Enable the Bundle - ------------------------- + This command requires you to have Composer installed globally, as explained + in the [installation chapter](https://getcomposer.org/doc/00-intro.md) + of the Composer documentation. - Then, enable the bundle by adding the following in the `app/AppKernel.php` - file of your project: + Step 2: Enable the Bundle + ------------------------- - ```php - \\(), + ); - new \\(), - ); + // ... + } // ... } + ``` - // ... - } - ``` + .. code-block:: rst + + Installation + ============ + + Step 1: Download the Bundle + --------------------------- + + Open a command console, enter your project directory and execute the + following command to download the latest stable version of this bundle: + + .. code-block:: bash + + $ composer require "~1" + + This command requires you to have Composer installed globally, as explained + in the `installation chapter`_ of the Composer documentation. + + Step 2: Enable the Bundle + ------------------------- + + Then, enable the bundle by adding it to the list of registered bundles + in the ``app/AppKernel.php`` file of your project: + + .. code-block:: php + + \\(), + ); + + // ... + } + + // ... + } + + .. _`installation chapter`: https://getcomposer.org/doc/00-intro.md -This template assumes that your bundle is in its ``1.x`` version. If not, change -the ``"~1"`` installation version accordingly (``"~2"``, ``"~3"``, etc.) +The example above assumes that you are installing the latest stable version of +the bundle, where you don't have to provide the package version number +(e.g. ``composer require friendsofsymfony/user-bundle``). If the installation +instructions refer to some past bundle version or to some unstable version, +include the version constraint (e.g. ``composer require friendsofsymfony/user-bundle "~2.0@dev"``). Optionally, you can add more installation steps (*Step 3*, *Step 4*, etc.) to explain other required installation tasks, such as registering routes or From 699e63740374ff53c8a53e0a5828ab0d08a45827 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 24 Jun 2015 10:01:20 +0200 Subject: [PATCH 0189/2667] Reworded the notice as a caution --- components/dom_crawler.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/components/dom_crawler.rst b/components/dom_crawler.rst index 55b7a55be45..3688e07c97c 100644 --- a/components/dom_crawler.rst +++ b/components/dom_crawler.rst @@ -231,7 +231,12 @@ and :phpclass:`DOMNode` objects: $html = $crawler->html(); The ``html`` method is new in Symfony 2.3. - In PHP < 5.3.6 it might return html entities which are not properly decoded. + + .. caution:: + + Due to an issue in PHP, the ``html()`` method returns wrongly decoded HTML + entities in PHP versions lower than 5.3.6 (for example, it returns ``•`` + instead of ``•``). Links ~~~~~ From 776eaee16020a4a4c5879091423291c2f8602d5a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 24 Jun 2015 16:21:59 +0200 Subject: [PATCH 0190/2667] Reworded as a definition list --- cookbook/bundles/best_practices.rst | 31 +++++++++++++++++------------ 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/cookbook/bundles/best_practices.rst b/cookbook/bundles/best_practices.rst index 81e3d688948..2600df7d237 100644 --- a/cookbook/bundles/best_practices.rst +++ b/cookbook/bundles/best_practices.rst @@ -388,19 +388,24 @@ Composer Metadata The ``composer.json`` file should include at least the following metadata: -* ``name``, which includes the vendor and the short bundle name. If you are - releasing the bundle on your own instead of on behalf of a company, use your - personal name (e.g. ``johnsmith/blog-bundle``). The bundle short name excludes - the vendor name and separates each word with an hyphen. For example: - ``AcmeBlogBundle`` is transformed into ``blog-bundle`` and ``AcmeSocialConnectBundle`` - is transformed into ``social-connect-bundle``; -* ``description``, a brief explanation of the purpose of the bundle; -* ``type``, use the ``symfony-bundle`` value; -* ``license``, ``MIT`` is the preferred license for Symfony bundles, but you - can use any other value. -* ``autoload``, this information is used by Symfony to load the classes of the - bundle. The `PSR-4`_ autoload standard is recommended for modern bundles, but - `PSR-0`_ standard is also supported. +``name`` + Consists of the vendor and the short bundle name. If you are releasing the + bundle on your own instead of on behalf of a company, use your personal name + (e.g. ``johnsmith/blog-bundle``). The bundle short name excludes the vendor + name and separates each word with an hyphen. For example: ``AcmeBlogBundle`` + is transformed into ``blog-bundle`` and ``AcmeSocialConnectBundle`` is + transformed into ``social-connect-bundle``. +``description`` + A brief explanation of the purpose of the bundle. +``type`` + Use the ``symfony-bundle`` value. +``license`` + ``MIT`` is the preferred license for Symfony bundles, but you can use any + other license. +``autoload`` + This information is used by Symfony to load the classes of the bundle. The + `PSR-4`_ autoload standard is recommended for modern bundles, but `PSR-0`_ + standard is also supported. In order to make it easier for developers to find your bundle, register it on `Packagist`_, the official repository for Composer packages. From 286631ddc3ba44059c44ac2ce23d123a9ac6d7b5 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 24 Jun 2015 16:51:05 +0200 Subject: [PATCH 0191/2667] Added a blank line beteen definition list items --- cookbook/bundles/best_practices.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cookbook/bundles/best_practices.rst b/cookbook/bundles/best_practices.rst index 2600df7d237..d273fe7ccf0 100644 --- a/cookbook/bundles/best_practices.rst +++ b/cookbook/bundles/best_practices.rst @@ -395,13 +395,17 @@ The ``composer.json`` file should include at least the following metadata: name and separates each word with an hyphen. For example: ``AcmeBlogBundle`` is transformed into ``blog-bundle`` and ``AcmeSocialConnectBundle`` is transformed into ``social-connect-bundle``. + ``description`` A brief explanation of the purpose of the bundle. + ``type`` Use the ``symfony-bundle`` value. + ``license`` ``MIT`` is the preferred license for Symfony bundles, but you can use any other license. + ``autoload`` This information is used by Symfony to load the classes of the bundle. The `PSR-4`_ autoload standard is recommended for modern bundles, but `PSR-0`_ From 3460adc1f2b0dbbf819e563e618bfeef36e8dcdc Mon Sep 17 00:00:00 2001 From: Norio Suzuki Date: Thu, 25 Jun 2015 07:25:05 +0900 Subject: [PATCH 0192/2667] Fixed namespace of Proxy. --- components/http_foundation/session_configuration.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/http_foundation/session_configuration.rst b/components/http_foundation/session_configuration.rst index 18295c702c0..33878afa906 100644 --- a/components/http_foundation/session_configuration.rst +++ b/components/http_foundation/session_configuration.rst @@ -274,18 +274,18 @@ further creates an extension point from where custom logic can be added that works independently of which handler is being wrapped inside. There are two kinds of save handler class proxies which inherit from -:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\AbstractProxy`: -they are :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeProxy` -and :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\SessionHandlerProxy`. +:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy`: +they are :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\NativeProxy` +and :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy`. :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage` automatically injects storage handlers into a save handler proxy unless already wrapped by one. -:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeProxy` +:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\NativeProxy` is used automatically under PHP 5.3 when internal PHP save handlers are specified using the ``Native*SessionHandler`` classes, while -:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\SessionHandlerProxy` +:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy` will be used to wrap any custom save handlers, that implement :phpclass:`SessionHandlerInterface`. From PHP 5.4 and above, all session handlers implement :phpclass:`SessionHandlerInterface` From 74267cdf612f079d2d72f9573330298aa14a6a18 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 25 Jun 2015 16:12:57 +0200 Subject: [PATCH 0193/2667] Fixes due to docbot linting docs --- reference/constraints/Length.rst | 6 +- reference/constraints/NotBlank.rst | 8 +-- reference/forms/twig_reference.rst | 92 ++++++++++++++++++------------ reference/forms/types/checkbox.rst | 6 +- reference/forms/types/choice.rst | 16 +++--- reference/forms/types/integer.rst | 8 +-- 6 files changed, 78 insertions(+), 58 deletions(-) diff --git a/reference/constraints/Length.rst b/reference/constraints/Length.rst index 82d30261827..455f8c882e1 100644 --- a/reference/constraints/Length.rst +++ b/reference/constraints/Length.rst @@ -115,9 +115,9 @@ min This required option is the "min" length value. Validation will fail if the given value's length is **less** than this min value. -It is important to notice that NULL values and empty strings are considered -valid no matter if the constraint required a minimum length. Validators are -triggered only if the value is not blank. +It is important to notice that NULL values and empty strings are considered +valid no matter if the constraint required a minimum length. Validators +are triggered only if the value is not blank. max ~~~ diff --git a/reference/constraints/NotBlank.rst b/reference/constraints/NotBlank.rst index f171526f047..c00e5fb1f6d 100644 --- a/reference/constraints/NotBlank.rst +++ b/reference/constraints/NotBlank.rst @@ -1,10 +1,10 @@ NotBlank ======== -Validates that a value is not blank, defined as not strictly ``false``, not -equal to a blank string and also not equal to ``null``. To force that a value -is simply not equal to ``null``, see the :doc:`/reference/constraints/NotNull` -constraint. +Validates that a value is not blank, defined as not strictly ``false``, +not equal to a blank string and also not equal to ``null``. To force that +a value is simply not equal to ``null``, see the +:doc:`/reference/constraints/NotNull` constraint. +----------------+------------------------------------------------------------------------+ | Applies to | :ref:`property or method ` | diff --git a/reference/forms/twig_reference.rst b/reference/forms/twig_reference.rst index 9abf4c27b4c..80010c8b914 100644 --- a/reference/forms/twig_reference.rst +++ b/reference/forms/twig_reference.rst @@ -4,15 +4,17 @@ Twig Template Form Function and Variable Reference ================================================== -When working with forms in a template, there are two powerful things at your -disposal: +When working with forms in a template, there are two powerful things at +your disposal: -* :ref:`Functions ` for rendering each part of a form -* :ref:`Variables ` for getting *any* information about any field +* :ref:`Functions ` for rendering each part + of a form; +* :ref:`Variables ` for getting *any* information + about any field. You'll use functions often to render your fields. Variables, on the other hand, are less commonly-used, but infinitely powerful since you can access -a fields label, id attribute, errors, and anything else about the field. +a fields label, id attribute, errors and anything else about the field. .. _reference-form-twig-functions: @@ -20,9 +22,9 @@ Form Rendering Functions ------------------------ This reference manual covers all the possible Twig functions available for -rendering forms. There are several different functions available, and each -is responsible for rendering a different part of a form (e.g. labels, errors, -widgets, etc). +rendering forms. There are several different functions available and +each is responsible for rendering a different part of a form (e.g. labels, +errors, widgets, etc). .. _reference-forms-twig-form: @@ -76,8 +78,8 @@ Renders the end tag of a form. {{ form_end(form) }} -This helper also outputs ``form_rest()`` unless you set ``render_rest`` to -false: +This helper also outputs ``form_rest()`` unless you set ``render_rest`` +to false: .. code-block:: jinja @@ -98,7 +100,11 @@ label you want to display as the second argument. {# The two following syntaxes are equivalent #} {{ form_label(form.name, 'Your Name', {'label_attr': {'class': 'foo'}}) }} - {{ form_label(form.name, null, {'label': 'Your name', 'label_attr': {'class': 'foo'}}) }} + + {{ form_label(form.name, null, { + 'label': 'Your name', + 'label_attr': {'class': 'foo'} + }) }} See ":ref:`twig-reference-form-variables`" to learn about the ``variables`` argument. @@ -122,8 +128,8 @@ Renders any errors for the given field. form_widget(view, variables) ---------------------------- -Renders the HTML widget of a given field. If you apply this to an entire form -or collection of fields, each underlying form row will be rendered. +Renders the HTML widget of a given field. If you apply this to an entire +form or collection of fields, each underlying form row will be rendered. .. code-block:: jinja @@ -181,8 +187,8 @@ form_enctype(view) .. note:: - This helper was deprecated in Symfony 2.3 and will be removed in Symfony 3.0. - You should use ``form_start()`` instead. + This helper was deprecated in Symfony 2.3 and will be removed in Symfony + 3.0. You should use ``form_start()`` instead. If the form contains at least one file upload field, this will render the required ``enctype="multipart/form-data"`` form attribute. It's always a @@ -204,7 +210,8 @@ selectedchoice(selected_value) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This test will check if the current choice is equal to the ``selected_value`` -or if the current choice is in the array (when ``selected_value`` is an array). +or if the current choice is in the array (when ``selected_value`` is an +array). .. code-block:: jinja @@ -221,7 +228,7 @@ More about Form Variables In almost every Twig function above, the final argument is an array of "variables" that are used when rendering that one part of the form. For example, the -following would render the "widget" for a field, and modify its attributes +following would render the "widget" for a field and modify its attributes to include a special class: .. code-block:: jinja @@ -242,41 +249,52 @@ Look at the ``form_label`` as an example: {% if not compound %} {% set label_attr = label_attr|merge({'for': id}) %} {% endif %} + {% if required %} - {% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %} + {% set label_attr = label_attr|merge({ + 'class': (label_attr.class|default('') ~ ' required')|trim + }) %} {% endif %} + {% if label is empty %} {% set label = name|humanize %} {% endif %} - {{ label|trans({}, translation_domain) }} + + {% endblock form_label %} -This block makes use of several variables: ``compound``, ``label_attr``, ``required``, -``label``, ``name`` and ``translation_domain``. -These variables are made available by the form rendering system. But more -importantly, these are the variables that you can override when calling ``form_label`` -(since in this example, you're rendering the label). +This block makes use of several variables: ``compound``, ``label_attr``, +``required``, ``label``, ``name`` and ``translation_domain``. These variables +are made available by the form rendering system. But more importantly, these +are the variables that you can override when calling ``form_label`` (since +in this example, you're rendering the label). The exact variables available to override depends on which part of the form you're rendering (e.g. label versus widget) and which field you're rendering -(e.g. a ``choice`` widget has an extra ``expanded`` option). If you get comfortable -with looking through `form_div_layout.html.twig`_, you'll always be able -to see what options you have available. +(e.g. a ``choice`` widget has an extra ``expanded`` option). If you get +comfortable with looking through `form_div_layout.html.twig`_, you'll always +be able to see what options you have available. .. tip:: Behind the scenes, these variables are made available to the ``FormView`` - object of your form when the Form component calls ``buildView`` and ``finishView`` - on each "node" of your form tree. To see what "view" variables a particular - field has, find the source code for the form field (and its parent fields) - and look at the above two functions. + object of your form when the Form component calls ``buildView`` and + ``finishView`` on each "node" of your form tree. To see what "view" + variables a particular field has, find the source code for the form + field (and its parent fields) and look at the above two functions. .. note:: If you're rendering an entire form at once (or an entire embedded form), the ``variables`` argument will only be applied to the form itself and - not its children. In other words, the following will **not** pass a "foo" - class attribute to all of the child fields in the form: + not its children. In other words, the following will **not** pass a + "foo" class attribute to all of the child fields in the form: .. code-block:: jinja @@ -292,10 +310,10 @@ The following variables are common to every field type. Certain field types may have even more variables and some variables here only really apply to certain types. -Assuming you have a ``form`` variable in your template, and you want to reference -the variables on the ``name`` field, accessing the variables is done by using -a public ``vars`` property on the :class:`Symfony\\Component\\Form\\FormView` -object: +Assuming you have a ``form`` variable in your template and you want to +reference the variables on the ``name`` field, accessing the variables is +done by using a public ``vars`` property on the +:class:`Symfony\\Component\\Form\\FormView` object: .. configuration-block:: diff --git a/reference/forms/types/checkbox.rst b/reference/forms/types/checkbox.rst index abef5a25330..b0c657a2add 100644 --- a/reference/forms/types/checkbox.rst +++ b/reference/forms/types/checkbox.rst @@ -4,9 +4,9 @@ checkbox Field Type =================== -Creates a single input checkbox. This should always be used for a field that -has a boolean value: if the box is checked, the field will be set to true, -if the box is unchecked, the value will be set to false. +Creates a single input checkbox. This should always be used for a field +that has a boolean value: if the box is checked, the field will be set to +true, if the box is unchecked, the value will be set to false. +-------------+------------------------------------------------------------------------+ | Rendered as | ``input`` ``checkbox`` field | diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index 82c18c6ff92..949e2b437d7 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -109,15 +109,17 @@ The ``choice_list`` option must be an instance of the ``ChoiceListInterface``. For more advanced cases, a custom class that implements the interface can be created to supply the choices. -With this option you can also allow float values to be selected as data. For example: - -.. code-block:: php +With this option you can also allow float values to be selected as data. +For example:: use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList; // ... $builder->add('status', 'choice', array( - 'choice_list' => new ChoiceList(array(1, 0.5, 0.1), array('Full', 'Half', 'Almost empty')) + 'choice_list' => new ChoiceList( + array(1, 0.5, 0.1), + array('Full', 'Half', 'Almost empty') + ) )); The ``status`` field created by the code above will be rendered as: @@ -130,9 +132,9 @@ The ``status`` field created by the code above will be rendered as: -But don't be confused! If ``Full`` is selected (value ``0`` in HTML), ``1`` will -be returned in your form. If ``Almost empty`` is selected (value ``2`` in HTML), -``0.1`` will be returned. +But don't be confused! If ``Full`` is selected (value ``0`` in HTML), ``1`` +will be returned in your form. If ``Almost empty`` is selected (value ``2`` +in HTML), ``0.1`` will be returned. .. include:: /reference/forms/types/options/empty_value.rst.inc diff --git a/reference/forms/types/integer.rst b/reference/forms/types/integer.rst index e6ea1895cb5..29f7609d6dd 100644 --- a/reference/forms/types/integer.rst +++ b/reference/forms/types/integer.rst @@ -4,10 +4,10 @@ integer Field Type ================== -Renders an input "number" field. Basically, this is a text field that's good -at handling data that's in an integer form. The input ``number`` field looks -like a text box, except that - if the user's browser supports HTML5 - it will -have some extra front-end functionality. +Renders an input "number" field. Basically, this is a text field that's +good at handling data that's in an integer form. The input ``number`` field +looks like a text box, except that - if the user's browser supports HTML5 +- it will have some extra front-end functionality. This field has different options on how to handle input values that aren't integers. By default, all non-integer values (e.g. 6.78) will round down From 63ccd0d938df53b4db492d523e0073b4bc16e863 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 25 Jun 2015 16:36:57 +0200 Subject: [PATCH 0194/2667] Finalize review of reference documentation --- reference/configuration/doctrine.rst | 126 ++++++++++++----------- reference/configuration/security.rst | 22 ++-- reference/configuration/swiftmailer.rst | 36 ++++--- reference/configuration/twig.rst | 12 ++- reference/configuration/web_profiler.rst | 11 +- reference/dic_tags.rst | 4 +- reference/events.rst | 26 ++--- reference/map.rst.inc | 8 +- 8 files changed, 133 insertions(+), 112 deletions(-) diff --git a/reference/configuration/doctrine.rst b/reference/configuration/doctrine.rst index 88e83838ba1..1a329f7aa57 100644 --- a/reference/configuration/doctrine.rst +++ b/reference/configuration/doctrine.rst @@ -324,8 +324,8 @@ you can control. The following configuration options exist for a mapping: type .... -One of ``annotation``, ``xml``, ``yml``, ``php`` or ``staticphp``. This specifies -which type of metadata type your mapping uses. +One of ``annotation``, ``xml``, ``yml``, ``php`` or ``staticphp``. This +specifies which type of metadata type your mapping uses. dir ... @@ -339,18 +339,18 @@ that exist in the DIC (for example ``%kernel.root_dir%``). prefix ...... -A common namespace prefix that all entities of this mapping share. This prefix -should never conflict with prefixes of other defined mappings otherwise some -of your entities cannot be found by Doctrine. This option defaults to the -bundle namespace + ``Entity``, for example for an application bundle called -AcmeHelloBundle prefix would be ``Acme\HelloBundle\Entity``. +A common namespace prefix that all entities of this mapping share. This +prefix should never conflict with prefixes of other defined mappings otherwise +some of your entities cannot be found by Doctrine. This option defaults +to the bundle namespace + ``Entity``, for example for an application bundle +called AcmeHelloBundle prefix would be ``Acme\HelloBundle\Entity``. alias ..... Doctrine offers a way to alias entity namespaces to simpler, shorter names -to be used in DQL queries or for Repository access. When using a bundle the -alias defaults to the bundle name. +to be used in DQL queries or for Repository access. When using a bundle +the alias defaults to the bundle name. is_bundle ......... @@ -358,8 +358,8 @@ is_bundle This option is a derived value from ``dir`` and by default is set to ``true`` if dir is relative proved by a ``file_exists()`` check that returns ``false``. It is ``false`` if the existence check returns ``true``. In this case an -absolute path was specified and the metadata files are most likely in a directory -outside of a bundle. +absolute path was specified and the metadata files are most likely in a +directory outside of a bundle. .. index:: single: Configuration; Doctrine DBAL @@ -448,14 +448,15 @@ The following block shows all possible configuration keys: .. note:: - The ``server_version`` option was added in Doctrine DBAL 2.5, which is used - by DoctrineBundle 1.3. The value of this option should match your database - server version (use ``postgres -V`` or ``psql -V`` command to find - your PostgreSQL version and ``mysql -V`` to get your MySQL version). + The ``server_version`` option was added in Doctrine DBAL 2.5, which + is used by DoctrineBundle 1.3. The value of this option should match + your database server version (use ``postgres -V`` or ``psql -V`` command + to find your PostgreSQL version and ``mysql -V`` to get your MySQL + version). - If you don't define this option and you haven't created your database yet, - you may get ``PDOException`` errors because Doctrine will try to guess the - database server version automatically and none is available. + If you don't define this option and you haven't created your database + yet, you may get ``PDOException`` errors because Doctrine will try to + guess the database server version automatically and none is available. If you want to configure multiple connections in YAML, put them under the ``connections`` key and give them a unique name: @@ -524,24 +525,26 @@ Keep in mind that you can't use both syntaxes at the same time. Custom Mapping Entities in a Bundle ----------------------------------- -Doctrine's ``auto_mapping`` feature loads annotation configuration from the -``Entity/`` directory of each bundle *and* looks for other formats (e.g. YAML, XML) -in the ``Resources/config/doctrine`` directory. +Doctrine's ``auto_mapping`` feature loads annotation configuration from +the ``Entity/`` directory of each bundle *and* looks for other formats (e.g. +YAML, XML) in the ``Resources/config/doctrine`` directory. -If you store metadata somewhere else in your bundle, you can define your own mappings, -where you tell Doctrine exactly *where* to look, along with some other configurations. +If you store metadata somewhere else in your bundle, you can define your +own mappings, where you tell Doctrine exactly *where* to look, along with +some other configurations. -If you're using the ``auto_mapping`` configuration, you just need to overwrite the -configurations you want. In this case it's important that the key of the mapping -configurations corresponds to the name of the bundle. +If you're using the ``auto_mapping`` configuration, you just need to overwrite +the configurations you want. In this case it's important that the key of +the mapping configurations corresponds to the name of the bundle. -For example, suppose you decide to store your ``XML`` configuration for ``AppBundle`` entities -in the ``@AppBundle/SomeResources/config/doctrine`` directory instead: +For example, suppose you decide to store your ``XML`` configuration for +``AppBundle`` entities in the ``@AppBundle/SomeResources/config/doctrine`` +directory instead: .. configuration-block:: .. code-block:: yaml - + doctrine: # ... orm: @@ -552,22 +555,22 @@ in the ``@AppBundle/SomeResources/config/doctrine`` directory instead: AppBundle: type: xml dir: SomeResources/config/doctrine - + .. code-block:: xml - + - + - + .. code-block:: php - + $container->loadFromExtension('doctrine', array( 'orm' => array( 'auto_mapping' => true, @@ -582,13 +585,14 @@ Mapping Entities Outside of a Bundle You can also create new mappings, for example outside of the Symfony folder. -For example, the following looks for entity classes in the ``App\Entity`` namespace in the -``src/Entity`` directory and gives them an ``App`` alias (so you can say things like ``App:Post``): +For example, the following looks for entity classes in the ``App\Entity`` +namespace in the ``src/Entity`` directory and gives them an ``App`` alias +(so you can say things like ``App:Post``): .. configuration-block:: .. code-block:: yaml - + doctrine: # ... orm: @@ -601,16 +605,16 @@ For example, the following looks for entity classes in the ``App\Entity`` namesp is_bundle: false prefix: App\Entity alias: App - + .. code-block:: xml - + - + - - + .. code-block:: php - + $container->loadFromExtension('doctrine', array( 'orm' => array( 'auto_mapping' => true, @@ -641,31 +645,33 @@ For example, the following looks for entity classes in the ``App\Entity`` namesp Detecting a Mapping Configuration Format ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If the ``type`` on the bundle configuration isn't set, -the DoctrineBundle will try to detect the correct mapping configuration format for -the bundle. +If the ``type`` on the bundle configuration isn't set, the DoctrineBundle +will try to detect the correct mapping configuration format for the bundle. -DoctrineBundle will look for files matching ``*.orm.[FORMAT]`` (e.g. ``Post.orm.yml``) -in the configured ``dir`` of your mapping (if you're mapping a bundle, then ``dir`` is -relative to the bundle's directory). +DoctrineBundle will look for files matching ``*.orm.[FORMAT]`` (e.g. +``Post.orm.yml``) in the configured ``dir`` of your mapping (if you're mapping +a bundle, then ``dir`` is relative to the bundle's directory). The bundle looks for (in this order) XML, YAML and PHP files. -Using the ``auto_mapping`` feature, every bundle can have only one configuration format. -The bundle will stop as soon as it locates one. +Using the ``auto_mapping`` feature, every bundle can have only one +configuration format. The bundle will stop as soon as it locates one. If it wasn't possible to determine a configuration format for a bundle, -the DoctrineBundle will check if there is an ``Entity`` folder in the bundle's root directory. -If the folder exist, Doctrine will fall back to using an annotation driver. +the DoctrineBundle will check if there is an ``Entity`` folder in the bundle's +root directory. If the folder exist, Doctrine will fall back to using an +annotation driver. -Default Value of dir +Default Value of Dir ~~~~~~~~~~~~~~~~~~~~ -If ``dir`` is not specified, then its default value depends on which configuration driver is being used. -For drivers that rely on the PHP files (annotation, staticphp) it will -be ``[Bundle]/Entity``. For drivers that are using configuration -files (XML, YAML, ...) it will be ``[Bundle]/Resources/config/doctrine``. +If ``dir`` is not specified, then its default value depends on which configuration +driver is being used. For drivers that rely on the PHP files (annotation, +staticphp) it will be ``[Bundle]/Entity``. For drivers that are using +configuration files (XML, YAML, ...) it will be +``[Bundle]/Resources/config/doctrine``. -If the ``dir`` configuration is set and the ``is_bundle`` configuration is ``true``, -the DoctrineBundle will prefix the ``dir`` configuration with the path of the bundle. +If the ``dir`` configuration is set and the ``is_bundle`` configuration +is ``true``, the DoctrineBundle will prefix the ``dir`` configuration with +the path of the bundle. .. _`DQL User Defined Functions`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/cookbook/dql-user-defined-functions.html diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index c3f174f4a98..544cc0ec865 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -4,10 +4,10 @@ SecurityBundle Configuration ("security") ========================================= -The security system is one of the most powerful parts of Symfony, and can +The security system is one of the most powerful parts of Symfony and can largely be controlled via its configuration. -Full default Configuration +Full Default Configuration -------------------------- The following is the full default configuration for the security system. @@ -306,8 +306,8 @@ post_only **type**: ``boolean`` **default**: ``true`` By default, you must submit your login form to the ``check_path`` URL as -a POST request. By setting this option to ``false``, you can send a GET request -to the ``check_path`` URL. +a POST request. By setting this option to ``false``, you can send a GET +request to the ``check_path`` URL. Redirecting after Login ~~~~~~~~~~~~~~~~~~~~~~~ @@ -328,7 +328,8 @@ Using the PBKDF2 Encoder: Security and Speed The `PBKDF2`_ encoder provides a high level of Cryptographic security, as recommended by the National Institute of Standards and Technology (NIST). -You can see an example of the ``pbkdf2`` encoder in the YAML block on this page. +You can see an example of the ``pbkdf2`` encoder in the YAML block on this +page. But using PBKDF2 also warrants a warning: using it (with a high number of iterations) slows down the process. Thus, PBKDF2 should be used with @@ -389,10 +390,11 @@ Using the BCrypt Password Encoder )); The ``cost`` can be in the range of ``4-31`` and determines how long a password -will be encoded. Each increment of ``cost`` *doubles* the time it takes to -encode a password. +will be encoded. Each increment of ``cost`` *doubles* the time it takes +to encode a password. -If you don't provide the ``cost`` option, the default cost of ``13`` is used. +If you don't provide the ``cost`` option, the default cost of ``13`` is +used. .. note:: @@ -418,8 +420,8 @@ Firewall Context Most applications will only need one :ref:`firewall `. But if your application *does* use multiple firewalls, you'll notice that if you're authenticated in one firewall, you're not automatically authenticated -in another. In other words, the systems don't share a common "context": each -firewall acts like a separate security system. +in another. In other words, the systems don't share a common "context": +each firewall acts like a separate security system. However, each firewall has an optional ``context`` key (which defaults to the name of the firewall), which is used when storing and retrieving security diff --git a/reference/configuration/swiftmailer.rst b/reference/configuration/swiftmailer.rst index ccadb7dcb69..bae46b9ba33 100644 --- a/reference/configuration/swiftmailer.rst +++ b/reference/configuration/swiftmailer.rst @@ -11,8 +11,9 @@ options, see `Full Default Configuration`_ The ``swiftmailer`` key configures Symfony's integration with Swift Mailer, which is responsible for creating and delivering email messages. -The following section lists all options that are available to configure a -mailer. It is also possible to configure several mailers (see `Using Multiple Mailers`_). +The following section lists all options that are available to configure +a mailer. It is also possible to configure several mailers (see +`Using Multiple Mailers`_). Configuration ------------- @@ -121,9 +122,9 @@ sender_address **type**: ``string`` -If set, all messages will be delivered with this address as the "return path" -address, which is where bounced messages should go. This is handled internally -by Swift Mailer's ``Swift_Plugins_ImpersonatePlugin`` class. +If set, all messages will be delivered with this address as the "return +path" address, which is where bounced messages should go. This is handled +internally by Swift Mailer's ``Swift_Plugins_ImpersonatePlugin`` class. antiflood ~~~~~~~~~ @@ -149,10 +150,10 @@ delivery_address **type**: ``string`` -If set, all email messages will be sent to this address instead of being sent -to their actual recipients. This is often useful when developing. For example, -by setting this in the ``config_dev.yml`` file, you can guarantee that all -emails sent during development go to a single account. +If set, all email messages will be sent to this address instead of being +sent to their actual recipients. This is often useful when developing. For +example, by setting this in the ``config_dev.yml`` file, you can guarantee +that all emails sent during development go to a single account. This uses ``Swift_Plugins_RedirectingPlugin``. Original recipients are available on the ``X-Swift-To``, ``X-Swift-Cc`` and ``X-Swift-Bcc`` headers. @@ -162,16 +163,17 @@ delivery_whitelist **type**: ``array`` -Used in combination with ``delivery_address``. If set, emails matching any of these -patterns will be delivered like normal, instead of being sent to ``delivery_address``. -For details, see :ref:`the cookbook entry. ` +Used in combination with ``delivery_address``. If set, emails matching any +of these patterns will be delivered like normal, instead of being sent to +``delivery_address``. For details, see +:ref:`the cookbook entry `. disable_delivery ~~~~~~~~~~~~~~~~ **type**: ``boolean`` **default**: ``false`` -If true, the ``transport`` will automatically be set to ``null``, and no +If true, the ``transport`` will automatically be set to ``null`` and no emails will actually be delivered. logging @@ -179,10 +181,10 @@ logging **type**: ``boolean`` **default**: ``%kernel.debug%`` -If true, Symfony's data collector will be activated for Swift Mailer and the -information will be available in the profiler. +If true, Symfony's data collector will be activated for Swift Mailer and +the information will be available in the profiler. -Full default Configuration +Full Default Configuration -------------------------- .. configuration-block:: @@ -240,7 +242,7 @@ Full default Configuration -Using multiple Mailers +Using Multiple Mailers ---------------------- You can configure multiple mailers by grouping them under the ``mailers`` diff --git a/reference/configuration/twig.rst b/reference/configuration/twig.rst index bad80fc4da2..2776df191d0 100644 --- a/reference/configuration/twig.rst +++ b/reference/configuration/twig.rst @@ -55,7 +55,15 @@ TwigBundle Configuration ("twig") xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd"> - + MyBundle::form.html.twig @@ -98,7 +106,7 @@ Configuration .. _config-twig-exception-controller: exception_controller -.................... +~~~~~~~~~~~~~~~~~~~~ **type**: ``string`` **default**: ``twig.controller.exception:showAction`` diff --git a/reference/configuration/web_profiler.rst b/reference/configuration/web_profiler.rst index 50ad25c9043..25e8d853cd9 100644 --- a/reference/configuration/web_profiler.rst +++ b/reference/configuration/web_profiler.rst @@ -4,7 +4,7 @@ WebProfilerBundle Configuration ("web_profiler") ================================================ -Full default Configuration +Full Default Configuration -------------------------- .. configuration-block:: @@ -13,14 +13,17 @@ Full default Configuration web_profiler: - # DEPRECATED, it is not useful anymore and can be removed safely from your configuration + # DEPRECATED, it is not useful anymore and can be removed + # safely from your configuration verbose: true - # display the web debug toolbar at the bottom of pages with a summary of profiler info + # display the web debug toolbar at the bottom of pages with + # a summary of profiler info toolbar: false position: bottom - # gives you the opportunity to look at the collected data before following the redirect + # gives you the opportunity to look at the collected data + # before following the redirect intercept_redirects: false .. code-block:: xml diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 47e8c43aebf..67a2a8aaa1b 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -552,8 +552,8 @@ article: :doc:`/cookbook/request/mime_type`. Core Event Listener Reference ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -For the reference of Event Listeners associated with each kernel event, see the -:doc:`Symfony Events Reference `. +For the reference of Event Listeners associated with each kernel event, +see the :doc:`Symfony Events Reference `. .. _dic-tags-kernel-event-subscriber: diff --git a/reference/events.rst b/reference/events.rst index 60740ac9fe0..dc32e2266b0 100644 --- a/reference/events.rst +++ b/reference/events.rst @@ -2,20 +2,20 @@ Symfony Framework Events ======================== When the Symfony Framework (or anything using the :class:`Symfony\\Component\\HttpKernel\\HttpKernel`) -handles a request, a few core events are dispatched so that you can add listeners -throughout the process. These are called the "kernel events". For a larger -explanation, see :doc:`/components/http_kernel/introduction`. +handles a request, a few core events are dispatched so that you can add +listeners throughout the process. These are called the "kernel events". +For a larger explanation, see :doc:`/components/http_kernel/introduction`. Kernel Events ------------- Each event dispatched by the kernel is a subclass of -:class:`Symfony\\Component\\HttpKernel\\Event\\KernelEvent`. This means that -each event has access to the following information: +:class:`Symfony\\Component\\HttpKernel\\Event\\KernelEvent`. This means +that each event has access to the following information: :method:`Symfony\\Component\\HttpKernel\\Event\\KernelEvent::getRequestType` - Returns the *type* of the request (``HttpKernelInterface::MASTER_REQUEST`` or - ``HttpKernelInterface::SUB_REQUEST``). + Returns the *type* of the request (``HttpKernelInterface::MASTER_REQUEST`` + or ``HttpKernelInterface::SUB_REQUEST``). :method:`Symfony\\Component\\HttpKernel\\Event\\KernelEvent::getKernel` Returns the Kernel handling the request. @@ -87,8 +87,8 @@ Listener Class Name This event is not used by the FrameworkBundle, but it can be used to implement a view sub-system. This event is called *only* if the Controller does *not* -return a ``Response`` object. The purpose of the event is to allow some other -return value to be converted into a ``Response``. +return a ``Response`` object. The purpose of the event is to allow some +other return value to be converted into a ``Response``. The value returned by the Controller is accessible via the ``getControllerResult`` method:: @@ -115,8 +115,8 @@ method:: **Event Class**: :class:`Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent` -The purpose of this event is to allow other systems to modify or replace the -``Response`` object after its creation:: +The purpose of this event is to allow other systems to modify or replace +the ``Response`` object after its creation:: public function onKernelResponse(FilterResponseEvent $event) { @@ -137,8 +137,8 @@ The FrameworkBundle registers several listeners: Fixes the Response ``Content-Type`` based on the request format. :class:`Symfony\\Component\\HttpKernel\\EventListener\\EsiListener` - Adds a ``Surrogate-Control`` HTTP header when the Response needs to be parsed - for ESI tags. + Adds a ``Surrogate-Control`` HTTP header when the Response needs to + be parsed for ESI tags. .. seealso:: diff --git a/reference/map.rst.inc b/reference/map.rst.inc index 0bf22aa3b44..174cdd908cc 100644 --- a/reference/map.rst.inc +++ b/reference/map.rst.inc @@ -1,9 +1,9 @@ * **Configuration Options** - Ever wondered what configuration options you have available to you in files - such as ``app/config/config.yml``? In this section, all the available configuration - is broken down by the key (e.g. ``framework``) that defines each possible - section of your Symfony configuration. + Ever wondered what configuration options you have available to you in + files such as ``app/config/config.yml``? In this section, all the available + configuration is broken down by the key (e.g. ``framework``) that defines + each possible section of your Symfony configuration. * :doc:`framework ` * :doc:`doctrine ` From 9a94a41b7d19c7dccd81f4d4283da1feb936fa2b Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 25 Jun 2015 16:38:29 +0200 Subject: [PATCH 0195/2667] Review Quick Tour articles once again --- quick_tour/the_big_picture.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index 1399307e3ef..1d61f21d5aa 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -106,12 +106,12 @@ Congratulations! Your first Symfony project is up and running! them are explained in the :ref:`Setting up Permissions ` section of the official book. - + If the welcome page does not seem to be rendering CSS or image assets, install them first: - + .. code-block:: bash - + $ php app/console assets:install When you are finished working on your Symfony application, you can stop From bdc5ef231332c89de76df38f0127c349868540a6 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jun 2015 10:21:44 +0200 Subject: [PATCH 0196/2667] Improved the explanation about the verbosity levels of the console --- components/console/introduction.rst | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 4868d70e069..3b1f26bd47f 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -173,22 +173,18 @@ Verbosity Levels The ``VERBOSITY_VERY_VERBOSE`` and ``VERBOSITY_DEBUG`` constants were introduced in version 2.3 -The console has 5 levels of verbosity. These are defined in the +The console has five levels of verbosity. These are defined in the :class:`Symfony\\Component\\Console\\Output\\OutputInterface`: -======================================= ================================== -Mode Value -======================================= ================================== -OutputInterface::VERBOSITY_QUIET Do not output any messages -OutputInterface::VERBOSITY_NORMAL The default verbosity level -OutputInterface::VERBOSITY_VERBOSE Increased verbosity of messages -OutputInterface::VERBOSITY_VERY_VERBOSE Informative non essential messages -OutputInterface::VERBOSITY_DEBUG Debug messages -======================================= ================================== - -You can specify the quiet verbosity level with the ``--quiet`` or ``-q`` -option. The ``--verbose`` or ``-v`` option is used when you want an increased -level of verbosity. +=========================================== ================================== ===================== +Value Meaning Console option +=========================================== ================================== ===================== +``OutputInterface::VERBOSITY_QUIET`` Do not output any messages ``-q`` or ``--quiet`` +``OutputInterface::VERBOSITY_NORMAL`` The default verbosity level (none) +``OutputInterface::VERBOSITY_VERBOSE`` Increased verbosity of messages ``-v`` +``OutputInterface::VERBOSITY_VERY_VERBOSE`` Informative non essential messages ``-vv`` +``OutputInterface::VERBOSITY_DEBUG`` Debug messages ``-vvv`` +=========================================== ================================== ====================== .. tip:: @@ -198,7 +194,7 @@ level of verbosity. It is possible to print a message in a command for only a specific verbosity level. For example:: - if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { $output->writeln(...); } From 38bc075d0a5935b94f1834a1184dabf3837d586a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jun 2015 10:33:13 +0200 Subject: [PATCH 0197/2667] Minor reword --- components/console/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 3b1f26bd47f..eb7ab40ad16 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -173,7 +173,7 @@ Verbosity Levels The ``VERBOSITY_VERY_VERBOSE`` and ``VERBOSITY_DEBUG`` constants were introduced in version 2.3 -The console has five levels of verbosity. These are defined in the +The console has five verbosity levels. These are defined in the :class:`Symfony\\Component\\Console\\Output\\OutputInterface`: =========================================== ================================== ===================== From 7d279e317eb15e1a14d66aab90bafce1239a1700 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 8 Jun 2015 13:59:06 +0200 Subject: [PATCH 0198/2667] added examples for squashing --- contributing/code/patches.rst | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/contributing/code/patches.rst b/contributing/code/patches.rst index 1f9bf7000e5..e73427985fc 100644 --- a/contributing/code/patches.rst +++ b/contributing/code/patches.rst @@ -387,7 +387,6 @@ convert many commits to one commit. To do this, use the rebase command: .. code-block:: bash $ git rebase -i upstream/master - $ git push --force origin BRANCH_NAME After you type this command, an editor will popup showing a list of commits: @@ -401,7 +400,29 @@ To squash all commits into the first one, remove the word ``pick`` before the second and the last commits, and replace it by the word ``squash`` or just ``s``. When you save, Git will start rebasing, and if successful, will ask you to edit the commit message, which by default is a listing of the commit -messages of all the commits. When you are finished, execute the push command. +messages of all the commits. + +Examples for "squashing" the first and the second commit into one commit: + +.. code-block:: text + + squash 1a31be6 first commit + squash 7fc64b4 second commit + pick 7d33018 third commit + +or + +.. code-block:: text + + s 1a31be6 first commit + s 7fc64b4 second commit + pick 7d33018 third commit + +When you are finished, execute the following push command: + +.. code-block:: bash + + $ git push --force origin BRANCH_NAME .. _ProGit: http://git-scm.com/book .. _GitHub: https://github.com/signup/free From 82fa4ae77bcc0c2d4010df5cc905389fe3630ec4 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Fri, 26 Jun 2015 11:07:22 +0200 Subject: [PATCH 0199/2667] Revert "added examples for squashing" This reverts commit 7d279e317eb15e1a14d66aab90bafce1239a1700. --- contributing/code/patches.rst | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/contributing/code/patches.rst b/contributing/code/patches.rst index e73427985fc..1f9bf7000e5 100644 --- a/contributing/code/patches.rst +++ b/contributing/code/patches.rst @@ -387,6 +387,7 @@ convert many commits to one commit. To do this, use the rebase command: .. code-block:: bash $ git rebase -i upstream/master + $ git push --force origin BRANCH_NAME After you type this command, an editor will popup showing a list of commits: @@ -400,29 +401,7 @@ To squash all commits into the first one, remove the word ``pick`` before the second and the last commits, and replace it by the word ``squash`` or just ``s``. When you save, Git will start rebasing, and if successful, will ask you to edit the commit message, which by default is a listing of the commit -messages of all the commits. - -Examples for "squashing" the first and the second commit into one commit: - -.. code-block:: text - - squash 1a31be6 first commit - squash 7fc64b4 second commit - pick 7d33018 third commit - -or - -.. code-block:: text - - s 1a31be6 first commit - s 7fc64b4 second commit - pick 7d33018 third commit - -When you are finished, execute the following push command: - -.. code-block:: bash - - $ git push --force origin BRANCH_NAME +messages of all the commits. When you are finished, execute the push command. .. _ProGit: http://git-scm.com/book .. _GitHub: https://github.com/signup/free From e4463169c98355dc315c0fd45113b8cb1764c615 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Wed, 24 Jun 2015 09:23:29 +0200 Subject: [PATCH 0200/2667] removed squashing stuff added explanation, why it is no longer necessary --- contributing/code/patches.rst | 1241 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/contributing/code/patches.rst b/contributing/code/patches.rst index 1f9bf7000e5..b1a7d0534b8 100644 --- a/contributing/code/patches.rst +++ b/contributing/code/patches.rst @@ -171,9 +171,6 @@ in mind the following: * Do atomic and logically separate commits (use the power of ``git rebase`` to have a clean and logical history); -* Squash irrelevant commits that are just about fixing coding standards or - fixing typos in your own code; - * Never fix coding standards in some existing code as it makes the code review more difficult; @@ -381,27 +378,10 @@ patch. Before re-submitting the patch, rebase with ``upstream/master`` or to avoid messing other branches in the repo (``--force`` tells Git that you really want to mess with things so do it carefully). -Often, moderators will ask you to "squash" your commits. This means you will -convert many commits to one commit. To do this, use the rebase command: - -.. code-block:: bash - - $ git rebase -i upstream/master - $ git push --force origin BRANCH_NAME - -After you type this command, an editor will popup showing a list of commits: - -.. code-block:: text - - pick 1a31be6 first commit - pick 7fc64b4 second commit - pick 7d33018 third commit - -To squash all commits into the first one, remove the word ``pick`` before the -second and the last commits, and replace it by the word ``squash`` or just -``s``. When you save, Git will start rebasing, and if successful, will ask -you to edit the commit message, which by default is a listing of the commit -messages of all the commits. When you are finished, execute the push command. +Moderators earlier asked you to "squash" your commits. This means you will +convert many commits to one commit. This is no longer necessary today, because +Symfony project uses a proprietary tool which automatically squashes all commits +before merging. .. _ProGit: http://git-scm.com/book .. _GitHub: https://github.com/signup/free From 839342d71d588179c21863c72f5445a97b296071 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 24 Jun 2015 11:10:45 +0200 Subject: [PATCH 0201/2667] Added information about the four sub-components of Security component --- components/security/introduction.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/components/security/introduction.rst b/components/security/introduction.rst index 982834e6ab6..75383aedbbf 100644 --- a/components/security/introduction.rst +++ b/components/security/introduction.rst @@ -21,6 +21,23 @@ You can install the component in 2 different ways: .. include:: /components/require_autoload.rst.inc +Starting from Symfony 2.4, the Security component is divided into four smaller +sub-components which can be used separately: + +``symfony/security-core`` + It provides all the common security features, from authentication to + authorization and from encoding passwords to loading users. + +``symfony/security-http`` + It integrates the core sub-component with the HTTP protocol to handle HTTP + requests and responses. + +``symfony/security-csrf`` + It provides protection against `CSRF attacks`_. + +``symfony/security-acl`` + It provides a fine grained permissions mechanism based on Access Control Lists. + Sections -------- @@ -30,3 +47,4 @@ Sections * :doc:`/components/security/secure_tools` .. _Packagist: https://packagist.org/packages/symfony/security +.. _`CSRF attacks`: https://en.wikipedia.org/wiki/Cross-site_request_forgery From d85691c7e94c3e7553308a2e4d2e1ca4a5d8952b Mon Sep 17 00:00:00 2001 From: WouterJ Date: Fri, 26 Jun 2015 11:19:43 +0200 Subject: [PATCH 0202/2667] [#5435] Removed notion of Symfony 2.4 --- components/security/introduction.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/security/introduction.rst b/components/security/introduction.rst index 75383aedbbf..ad6a44ae692 100644 --- a/components/security/introduction.rst +++ b/components/security/introduction.rst @@ -21,8 +21,8 @@ You can install the component in 2 different ways: .. include:: /components/require_autoload.rst.inc -Starting from Symfony 2.4, the Security component is divided into four smaller -sub-components which can be used separately: +The Security component is divided into four smaller sub-components which can be +used separately: ``symfony/security-core`` It provides all the common security features, from authentication to F438 From a5f0eec0dfc3e147057f5de258f2cadb0c66006b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jun 2015 11:24:24 +0200 Subject: [PATCH 0203/2667] Documented the "auto_alias" feature --- reference/dic_tags.rst | 100 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index e4db9851aaa..84b7d96cd59 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -22,6 +22,7 @@ Tag Name Usage `assetic.formula_resource`_ Adds a resource to the current asset manager `assetic.templating.php`_ Remove this service if PHP templating is disabled `assetic.templating.twig`_ Remove this service if Twig templating is disabled +`auto_alias`_ Define aliases based on the value of container parameters `console.command`_ Add a command `data_collector`_ Create a class that collects custom data for the profiler `doctrine.event_listener`_ Add a Doctrine event listener @@ -227,6 +228,105 @@ assetic.templating.twig The tagged service will be removed from the container if ``framework.templating.engines`` config section does not contain ``twig``. +auto_alias +---------- + +**Purpose**: Define aliases based on the value of container parameters + +Consider the following configuration that defines three different but related +services: + +.. configuration-block:: + + .. code-block:: yaml + + services: + app.mysql_lock: + class: AppBundle\Lock\MysqlLock + app.postgresql_lock: + class: AppBundle\Lock\PostgresqlLock + app.sqlite_lock: + class: AppBundle\Lock\SqliteLock + + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + $container + ->register('app.mysql_lock', 'AppBundle\Lock\MysqlLock') + ->register('app.postgresql_lock', 'AppBundle\Lock\PostgresqlLock') + ->register('app.sqlite_lock', 'AppBundle\Lock\SqliteLock') + ; + +Instead of dealing with these three services, your application needs a generic +``app.lock`` service. This service must be an alias to any of the other services. +Thanks to the ``auto_alias`` option, you can automatically create that alias +based on the value of a configuration parameter. + +Considering that a configuration parameter called ``database_type`` exists, +the generic ``app.lock`` service can be defined as follows: + +.. configuration-block:: + + .. code-block:: yaml + + services: + app.mysql_lock: + class: AppBundle\Lock\MysqlLock + app.postgresql_lock: + class: AppBundle\Lock\PostgresqlLock + app.sqlite_lock: + class: AppBundle\Lock\SqliteLock + app.lock: + tags: + - { name: auto_alias, format: "%database_type%.lock" } + + .. code-block:: xml + + + + + + + + + + + + + + + + .. code-block:: php + + $container + ->register('app.mysql_lock', 'AppBundle\Lock\MysqlLock') + ->register('app.postgresql_lock', 'AppBundle\Lock\PostgresqlLock') + ->register('app.sqlite_lock', 'AppBundle\Lock\SqliteLock') + + ->register('app.lock') + ->addTag('auto_alias', array('format' => '%database_type%.lock')) + ; + +The ``format`` parameter defines the expression used to construct the name of +the service to alias. This expression can use any container parameter (as usual, +wrapping their names with ``%`` characters). + console.command --------------- From 69152e7036d1d309380c2022a8f85a662d3ad08b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jun 2015 11:37:48 +0200 Subject: [PATCH 0204/2667] Added the "versionadded: 2.7" directive --- reference/dic_tags.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 84b7d96cd59..34a7952b3e7 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -231,6 +231,9 @@ The tagged service will be removed from the container if auto_alias ---------- +.. versionadded:: 2.7 + The ``auto_alias`` tag was introduced in Symfony 2.7. + **Purpose**: Define aliases based on the value of container parameters Consider the following configuration that defines three different but related From e24f77ed372b0086cd74279647de90dcf6842f7c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jun 2015 11:38:31 +0200 Subject: [PATCH 0205/2667] Removed an extra blank line --- reference/dic_tags.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 34a7952b3e7..1a9cd187814 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -251,7 +251,6 @@ services: app.sqlite_lock: class: AppBundle\Lock\SqliteLock - .. code-block:: xml From 4c50cb07b999c5b58abb673484dd18114dcc8442 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jun 2015 12:09:09 +0200 Subject: [PATCH 0206/2667] Fixed some errors and added a new note --- reference/dic_tags.rst | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 1a9cd187814..20794ff3319 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -288,13 +288,16 @@ the generic ``app.lock`` service can be defined as follows: services: app.mysql_lock: class: AppBundle\Lock\MysqlLock + public: false app.postgresql_lock: class: AppBundle\Lock\PostgresqlLock + public: false app.sqlite_lock: class: AppBundle\Lock\SqliteLock + public: false app.lock: tags: - - { name: auto_alias, format: "%database_type%.lock" } + - { name: auto_alias, format: "app.%database_type%.lock" } .. code-block:: xml @@ -304,12 +307,15 @@ the generic ``app.lock`` service can be defined as follows: xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - + + + - + @@ -317,18 +323,25 @@ the generic ``app.lock`` service can be defined as follows: .. code-block:: php $container - ->register('app.mysql_lock', 'AppBundle\Lock\MysqlLock') - ->register('app.postgresql_lock', 'AppBundle\Lock\PostgresqlLock') - ->register('app.sqlite_lock', 'AppBundle\Lock\SqliteLock') + ->register('app.mysql_lock', 'AppBundle\Lock\MysqlLock')->setPublic(false) + ->register('app.postgresql_lock', 'AppBundle\Lock\PostgresqlLock')->setPublic(false) + ->register('app.sqlite_lock', 'AppBundle\Lock\SqliteLock')->setPublic(false) ->register('app.lock') - ->addTag('auto_alias', array('format' => '%database_type%.lock')) + ->addTag('auto_alias', array('format' => 'app.%database_type%.lock')) ; The ``format`` parameter defines the expression used to construct the name of the service to alias. This expression can use any container parameter (as usual, wrapping their names with ``%`` characters). +.. note:: + + When using the ``auto_alias`` tag is not mandatory to define the aliased + services as private. However, doing that (like in the above example) makes + sense most of the times to prevent accessing those services directly instead + of using the generic service. + console.command --------------- From f2fa53ba96abcefbe3d9603613e9453c3c9554fe Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 26 Jun 2015 09:30:54 +0200 Subject: [PATCH 0207/2667] remove mailing list and forum references As Stack Overflow is now the preferred web based support platform and both the mailing list and the forum will be shut down, we should not refer to them anymore. --- contributing/code/bugs.rst | 9 ++++----- contributing/code/security.rst | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/contributing/code/bugs.rst b/contributing/code/bugs.rst index 869b830491d..d4c26b9bee6 100644 --- a/contributing/code/bugs.rst +++ b/contributing/code/bugs.rst @@ -14,8 +14,8 @@ Before submitting a bug: * Double-check the official :doc:`documentation ` to see if you're not misusing the framework; -* Ask for assistance on the `users mailing-list`_, the `forum`_, or on the - #symfony `IRC channel`_ if you're not sure if your issue is really a bug. +* Ask for assistance on `Stack Overflow`_ or on the #symfony `IRC channel`_ + if you're not sure if your issue is really a bug. If your problem definitely looks like a bug, report it using the official bug `tracker`_ and follow some basic rules: @@ -34,8 +34,7 @@ If your problem definitely looks like a bug, report it using the official bug * *(optional)* Attach a :doc:`patch `. -.. _users mailing-list: http://groups.google.com/group/symfony2 -.. _forum: http://forum.symfony-project.org/ -.. _IRC channel: irc://irc.freenode.net/symfony +.. _`Stack Overflow`: http://stackoverflow.com/questions/tagged/symfony2 +.. _IRC channel: http://symfony.com/irc .. _tracker: https://github.com/symfony/symfony/issues .. _Symfony Standard Edition: https://github.com/symfony/symfony-standard/ diff --git a/contributing/code/security.rst b/contributing/code/security.rst index abfc08cb360..f523616167c 100644 --- a/contributing/code/security.rst +++ b/contributing/code/security.rst @@ -9,9 +9,9 @@ Reporting a Security Issue -------------------------- If you think that you have found a security issue in Symfony, don't use the -mailing-list or the bug tracker and don't publish it publicly. Instead, all -security issues must be sent to **security [at] symfony.com**. Emails sent to -this address are forwarded to the Symfony core-team private mailing-list. +bug tracker and don't publish it publicly. Instead, all security issues must +be sent to **security [at] symfony.com**. Emails sent to this address are +forwarded to the Symfony core-team private mailing-list. Resolving Process ----------------- From e15eb217c7551a63eb2ef3b3e9982c6c5058a095 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 26 Jun 2015 17:51:28 -0400 Subject: [PATCH 0208/2667] [#5014] Minor tweaks! --- cookbook/bundles/best_practices.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/bundles/best_practices.rst b/cookbook/bundles/best_practices.rst index d273fe7ccf0..02622aab9fd 100644 --- a/cookbook/bundles/best_practices.rst +++ b/cookbook/bundles/best_practices.rst @@ -98,7 +98,7 @@ The basic directory structure of an AcmeBlogBundle must read as follows: that automated tools can rely on: * ``AcmeBlogBundle.php``: This is the class that transforms a plain directory - into a Symfony bundle; + into a Symfony bundle (change this to your bundle's name); * ``README.md``: This file contains the basic description of the bundle and it usually shows some basic examples and links to its full documentation (it can use any of the markup formats supported by GitHub, such as ``README.rst``); @@ -151,7 +151,7 @@ Commands, Helpers, Listeners, and Controllers. Classes that connect to the event dispatcher should be suffixed with ``Listener``. -Exceptions classes should be stored in an ``Exception`` sub-namespace. +Exception classes should be stored in an ``Exception`` sub-namespace. Vendors ------- From ab87e62928e220ef463c782a7dfccbfb86c8509a Mon Sep 17 00:00:00 2001 From: Hari KT Date: Sat, 20 Jun 2015 22:32:58 +0530 Subject: [PATCH 0209/2667] Import Psr LogLevel --- components/console/logger.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/components/console/logger.rst b/components/console/logger.rst index cd3a4fb0caf..521a4a972cf 100644 --- a/components/console/logger.rst +++ b/components/console/logger.rst @@ -84,6 +84,7 @@ The association between the log level and the verbosity can be configured through the second parameter of the :class:`Symfony\\Component\\Console\\ConsoleLogger` constructor:: + use Psr\Log\LogLevel; // ... $verbosityLevelMap = array( LogLevel::NOTICE => OutputInterface::VERBOSITY_NORMAL, From 8b8ba03675c9fc77f73b04745c9ccbc34100e483 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 27 Jun 2015 00:42:51 +0200 Subject: [PATCH 0210/2667] [#5418] Let the code breath --- components/console/logger.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/components/console/logger.rst b/components/console/logger.rst index 521a4a972cf..8278ad8460a 100644 --- a/components/console/logger.rst +++ b/components/console/logger.rst @@ -86,6 +86,7 @@ constructor:: use Psr\Log\LogLevel; // ... + $verbosityLevelMap = array( LogLevel::NOTICE => OutputInterface::VERBOSITY_NORMAL, LogLevel::INFO => OutputInterface::VERBOSITY_NORMAL, From 176cdc6bc99f3b431286afdfe7edebf99c0493b0 Mon Sep 17 00:00:00 2001 From: aykin Date: Wed, 15 Apr 2015 10:27:16 +0300 Subject: [PATCH 0211/2667] Update the_controller.rst The sample code does not work due to an array o string conversion exception. The documentation needs to be updated to reflect the change --- quick_tour/the_controller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quick_tour/the_controller.rst b/quick_tour/the_controller.rst index 028fe3d75d9..4dc4144be37 100644 --- a/quick_tour/the_controller.rst +++ b/quick_tour/the_controller.rst @@ -331,7 +331,7 @@ And you can display the flash message in the template like this: .. code-block:: html+jinja
- {{ app.session.flashbag.get('notice') }} + {{ app.session.flashbag.get('notice')[0] }}
Final Thoughts From c2676ae9fa2ab904b0e1545c50f4103298ded9fb Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 23 Jun 2015 17:42:59 +0200 Subject: [PATCH 0212/2667] Improved the code used to display flash notices --- quick_tour/the_controller.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/quick_tour/the_controller.rst b/quick_tour/the_controller.rst index 4dc4144be37..d3bde2ed14b 100644 --- a/quick_tour/the_controller.rst +++ b/quick_tour/the_controller.rst @@ -330,9 +330,11 @@ And you can display the flash message in the template like this: .. code-block:: html+jinja -
- {{ app.session.flashbag.get('notice')[0] }} -
+ {% for flashMessage in app.session.flashbag.get('notice') %} +
+ {{ flashMessage }} +
+ {% endfor %} Final Thoughts -------------- From 10fc1015b4b04fa8c4c86bbe27fb76e80dea3ab8 Mon Sep 17 00:00:00 2001 From: Bocharsky Victor Date: Mon, 22 Jun 2015 00:26:40 +0300 Subject: [PATCH 0213/2667] Fix invalid method name Replace `enableMagicCallEnabled` invalid method name with `enableMagicCall` correct one --- components/property_access/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/property_access/introduction.rst b/components/property_access/introduction.rst index 8d559948418..68e493f0d5f 100644 --- a/components/property_access/introduction.rst +++ b/components/property_access/introduction.rst @@ -223,7 +223,7 @@ enable this feature by using :class:`Symfony\\Component\\PropertyAccess\\Propert .. caution:: The ``__call`` feature is disabled by default, you can enable it by calling - :method:`PropertyAccessorBuilder::enableMagicCallEnabled` + :method:`PropertyAccessorBuilder::enableMagicCall` see `Enable other Features`_. Writing to Arrays From 733c607f05689af694d5809813497b63b6f593d1 Mon Sep 17 00:00:00 2001 From: Hari KT Date: Sat, 20 Jun 2015 20:10:28 +0530 Subject: [PATCH 0214/2667] Add use statement for InputDefinition --- components/console/console_arguments.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/components/console/console_arguments.rst b/components/console/console_arguments.rst index fa56c141353..894680b684a 100644 --- a/components/console/console_arguments.rst +++ b/components/console/console_arguments.rst @@ -14,6 +14,7 @@ Have a look at the following command that has three options:: use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; + use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; From 10898c9dfafbded4c03b76d33ab7a87c04a1486d Mon Sep 17 00:00:00 2001 From: Henry Snoek Date: Sat, 23 May 2015 11:09:11 +0200 Subject: [PATCH 0215/2667] 5177 use dump() instead of echo in examples Conflicts: book/doctrine.rst components/expression_language/caching.rst components/expression_language/extending.rst components/expression_language/introduction.rst components/expression_language/syntax.rst components/serializer.rst components/translation/usage.rst --- book/doctrine.rst | 2 +- book/translation.rst | 4 +-- components/event_dispatcher/generic_event.rst | 5 +-- components/event_dispatcher/introduction.rst | 2 +- components/form/introduction.rst | 4 +-- components/http_foundation/introduction.rst | 4 +-- components/intl.rst | 12 +++---- components/property_access/introduction.rst | 36 +++++++++---------- components/security/authorization.rst | 5 +-- components/translation/custom_formats.rst | 3 +- components/translation/usage.rst | 6 ++-- cookbook/bundles/remove.rst | 2 +- 12 files changed, 44 insertions(+), 41 deletions(-) diff --git a/book/doctrine.rst b/book/doctrine.rst index fe2a1c6758f..4edc451946e 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -1204,7 +1204,7 @@ to the given ``Category`` object via their ``category_id`` value. $category = $product->getCategory(); // prints "Proxies\AppBundleEntityCategoryProxy" - echo get_class($category); + dump(get_class($category)); This proxy object extends the true ``Category`` object, and looks and acts exactly like it. The difference is that, by using a proxy object, diff --git a/book/translation.rst b/book/translation.rst index 863033ff66a..f3be3a70e16 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -12,11 +12,11 @@ wrapping each with a function capable of translating the text (or "message") into the language of the user:: // text will *always* print out in English - echo 'Hello World'; + dump('Hello World'); // text can be translated into the end-user's language or // default to English - echo $translator->trans('Hello World'); + dump($translator->trans('Hello World')); .. note:: diff --git a/components/event_dispatcher/generic_event.rst b/components/event_dispatcher/generic_event.rst index 3be0b9bb876..73ade2a1c8e 100644 --- a/components/event_dispatcher/generic_event.rst +++ b/components/event_dispatcher/generic_event.rst @@ -75,7 +75,7 @@ the event arguments:: ); $dispatcher->dispatch('foo', $event); - echo $event['counter']; + dump($event['counter']); class FooListener { @@ -96,7 +96,7 @@ Filtering data:: $event = new GenericEvent($subject, array('data' => 'Foo')); $dispatcher->dispatch('foo', $event); - echo $event['data']; + dump($event['data']); class FooListener { @@ -105,3 +105,4 @@ Filtering data:: $event['data'] = strtolower($event['data']); } } + diff --git a/components/event_dispatcher/introduction.rst b/components/event_dispatcher/introduction.rst index 49036aba7a1..e2c863bcafa 100644 --- a/components/event_dispatcher/introduction.rst +++ b/components/event_dispatcher/introduction.rst @@ -635,7 +635,7 @@ part of the listener's processing logic:: { public function myEventListener(Event $event) { - echo $event->getName(); + // ... do something with the event name } } diff --git a/components/form/introduction.rst b/components/form/introduction.rst index 01511f2de83..931dc87fdb5 100644 --- a/components/form/introduction.rst +++ b/components/form/introduction.rst @@ -396,9 +396,9 @@ is created from the form factory. ->add('dueDate', 'date') ->getForm(); - echo $twig->render('new.html.twig', array( + dump($twig->render('new.html.twig', array( 'form' => $form->createView(), - )); + ))); .. code-block:: php-symfony diff --git a/components/http_foundation/introduction.rst b/components/http_foundation/introduction.rst index 9f931553a96..2640459e8c9 100644 --- a/components/http_foundation/introduction.rst +++ b/components/http_foundation/introduction.rst @@ -415,10 +415,10 @@ represented by a PHP callable instead of a string:: $response = new StreamedResponse(); $response->setCallback(function () { - echo 'Hello World'; + dump('Hello World'); flush(); sleep(2); - echo 'Hello World'; + dump('Hello World'); flush(); }); $response->send(); diff --git a/components/intl.rst b/components/intl.rst index eb10a717e9a..1a8dc20573e 100644 --- a/components/intl.rst +++ b/components/intl.rst @@ -217,7 +217,7 @@ This class currently only works with the `intl extension`_ installed:: $reader = new BinaryBundleReader(); $data = $reader->read('/path/to/bundle', 'en'); - echo $data['Data']['entry1']; + dump($data['Data']['entry1']); PhpBundleReader ~~~~~~~~~~~~~~~ @@ -231,7 +231,7 @@ object:: $reader = new PhpBundleReader(); $data = $reader->read('/path/to/bundle', 'en'); - echo $data['Data']['entry1']; + dump($data['Data']['entry1']); BufferedBundleReader ~~~~~~~~~~~~~~~~~~~~ @@ -272,10 +272,10 @@ returned:: $data = $reader->read('/path/to/bundle', 'en'); // Produces an error if the key "Data" does not exist - echo $data['Data']['entry1']; + dump($data['Data']['entry1']); // Returns null if the key "Data" does not exist - echo $reader->readEntry('/path/to/bundle', 'en', array('Data', 'entry1')); + dump($reader->readEntry('/path/to/bundle', 'en', array('Data', 'entry1'))); Additionally, the :method:`Symfony\\Component\\Intl\\ResourceBundle\\Reader\\StructuredBundleReaderInterface::readEntry` @@ -286,12 +286,12 @@ multi-valued entries (arrays), the values of the more specific and the fallback locale will be merged. In order to suppress this behavior, the last parameter ``$fallback`` can be set to ``false``:: - echo $reader->readEntry( + dump($reader->readEntry( '/path/to/bundle', 'en', array('Data', 'entry1'), false - ); + )); Accessing ICU Data ------------------ diff --git a/components/property_access/introduction.rst b/components/property_access/introduction.rst index 68e493f0d5f..1b1f5179b75 100644 --- a/components/property_access/introduction.rst +++ b/components/property_access/introduction.rst @@ -51,8 +51,8 @@ method. This is done using the index notation that is used in PHP:: 'first_name' => 'Wouter', ); - echo $accessor->getValue($person, '[first_name]'); // 'Wouter' - echo $accessor->getValue($person, '[age]'); // null + dump($accessor->getValue($person, '[first_name]')); // 'Wouter' + dump($accessor->getValue($person, '[age]')); // null As you can see, the method will return ``null`` if the index does not exists. @@ -68,8 +68,8 @@ You can also use multi dimensional arrays:: ) ); - echo $accessor->getValue($persons, '[0][first_name]'); // 'Wouter' - echo $accessor->getValue($persons, '[1][first_name]'); // 'Ryan' + dump($accessor->getValue($persons, '[0][first_name]')); // 'Wouter' + dump($accessor->getValue($persons, '[1][first_name]')); // 'Ryan' Reading from Objects -------------------- @@ -86,13 +86,13 @@ To read from properties, use the "dot" notation:: $person = new Person(); $person->firstName = 'Wouter'; - echo $accessor->getValue($person, 'firstName'); // 'Wouter' + dump($accessor->getValue($person, 'firstName')); // 'Wouter' $child = new Person(); $child->firstName = 'Bar'; $person->children = array($child); - echo $accessor->getValue($person, 'children[0].firstName'); // 'Bar' + dump($accessor->getValue($person, 'children[0].firstName')); // 'Bar' .. caution:: @@ -122,7 +122,7 @@ property name (``first_name`` becomes ``FirstName``) and prefixes it with $person = new Person(); - echo $accessor->getValue($person, 'first_name'); // 'Wouter' + dump($accessor->getValue($person, 'first_name')); // 'Wouter' Using Hassers/Issers ~~~~~~~~~~~~~~~~~~~~ @@ -151,10 +151,10 @@ getters, this means that you can do something like this:: $person = new Person(); if ($accessor->getValue($person, 'author')) { - echo 'He is an author'; + dump('He is an author'); } if ($accessor->getValue($person, 'children')) { - echo 'He has children'; + dump('He has children'); } This will produce: ``He is an author`` @@ -179,7 +179,7 @@ The ``getValue`` method can also use the magic ``__get`` method:: $person = new Person(); - echo $accessor->getValue($person, 'Wouter'); // array(...) + dump($accessor->getValue($person, 'Wouter')); // array(...) Magic ``__call()`` Method ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -215,7 +215,7 @@ enable this feature by using :class:`Symfony\\Component\\PropertyAccess\\Propert ->enableMagicCall() ->getPropertyAccessor(); - echo $accessor->getValue($person, 'wouter'); // array(...) + dump($accessor->getValue($person, 'wouter')); // array(...) .. versionadded:: 2.3 The use of magic ``__call()`` method was introduced in Symfony 2.3. @@ -239,9 +239,9 @@ method:: $accessor->setValue($person, '[first_name]', 'Wouter'); - echo $accessor->getValue($person, '[first_name]'); // 'Wouter' + dump($accessor->getValue($person, '[first_name]')); // 'Wouter' // or - // echo $person['first_name']; // 'Wouter' + // dump($person['first_name']); // 'Wouter' Writing to Objects ------------------ @@ -275,9 +275,9 @@ can use setters, the magic ``__set`` method or properties to set values:: $accessor->setValue($person, 'lastName', 'de Jong'); $accessor->setValue($person, 'children', array(new Person())); - echo $person->firstName; // 'Wouter' - echo $person->getLastName(); // 'de Jong' - echo $person->children; // array(Person()); + dump($person->firstName); // 'Wouter' + dump($person->getLastName()); // 'de Jong' + dump($person->children); // array(Person()); You can also use ``__call`` to set values but you need to enable the feature, see `Enable other Features`_. @@ -313,7 +313,7 @@ see `Enable other Features`_. $accessor->setValue($person, 'wouter', array(...)); - echo $person->getWouter(); // array(...) + dump($person->getWouter()); // array(...) Mixing Objects and Arrays ------------------------- @@ -345,7 +345,7 @@ You can also mix objects and arrays:: $accessor->setValue($person, 'children[0].firstName', 'Wouter'); // equal to $person->getChildren()[0]->firstName = 'Wouter' - echo 'Hello '.$accessor->getValue($person, 'children[0].firstName'); // 'Wouter' + dump('Hello '.$accessor->getValue($person, 'children[0].firstName')); // 'Wouter' // equal to $person->getChildren()[0]->firstName Enable other Features diff --git a/components/security/authorization.rst b/components/security/authorization.rst index 9c3000a429e..5032b91340c 100644 --- a/components/security/authorization.rst +++ b/components/security/authorization.rst @@ -186,8 +186,8 @@ first constructor argument:: $role = new Role('ROLE_ADMIN'); - // will echo 'ROLE_ADMIN' - echo $role->getRole(); + // will show 'ROLE_ADMIN' + dump($role->getRole()); .. note:: @@ -247,3 +247,4 @@ decision manager:: if (!$securityContext->isGranted('ROLE_ADMIN')) { throw new AccessDeniedException(); } + diff --git a/components/translation/custom_formats.rst b/components/translation/custom_formats.rst index 4378d249f86..793d118c549 100644 --- a/components/translation/custom_formats.rst +++ b/components/translation/custom_formats.rst @@ -63,7 +63,7 @@ Once created, it can be used as any other loader:: $translator->addResource('my_format', __DIR__.'/translations/messages.txt', 'fr_FR'); - echo $translator->trans('welcome'); + dump($translator->trans('welcome')); It will print *"accueil"*. @@ -116,3 +116,4 @@ YAML file are dumped into a text file with the custom format:: $dumper = new MyFormatDumper(); $dumper->dump($catalogue, array('path' => __DIR__.'/dumps')); + diff --git a/components/translation/usage.rst b/components/translation/usage.rst index 57c1c4b3e49..55cb44ae832 100644 --- a/components/translation/usage.rst +++ b/components/translation/usage.rst @@ -15,7 +15,7 @@ Imagine you want to translate the string *"Symfony is great"* into French:: 'Symfony is great!' => 'J\'aime Symfony!', ), 'fr_FR'); - echo $translator->trans('Symfony is great!'); + dump($translator->trans('Symfony is great!')); In this example, the message *"Symfony is great!"* will be translated into the locale set in the constructor (``fr_FR``) if the message exists in one of @@ -31,7 +31,7 @@ Sometimes, a message containing a variable needs to be translated:: // ... $translated = $translator->trans('Hello '.$name); - echo $translated; + dump($translated); However, creating a translation for this string is impossible since the translator will try to look up the exact message, including the variable portions @@ -45,7 +45,7 @@ variable with a "placeholder":: array('%name%' => $name) ); - echo $translated; + dump($translated); Symfony will now look for a translation of the raw message (``Hello %name%``) and *then* replace the placeholders with their values. Creating a translation diff --git a/cookbook/bundles/remove.rst b/cookbook/bundles/remove.rst index 407ee421aa4..2cc87decf61 100644 --- a/cookbook/bundles/remove.rst +++ b/cookbook/bundles/remove.rst @@ -79,7 +79,7 @@ can remove the ``Acme`` directory as well. :method:`Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface::getPath` method to get the path of the bundle:: - echo $this->container->get('kernel')->getBundle('AcmeDemoBundle')->getPath(); + dump($this->container->get('kernel')->getBundle('AcmeDemoBundle')->getPath()); 3.1 Remove Bundle Assets ~~~~~~~~~~~~~~~~~~~~~~~~ From 78c9b7cdaeb781fd603374b8fcbf2a5a7354482c Mon Sep 17 00:00:00 2001 From: Henry Snoek Date: Sat, 23 May 2015 11:16:05 +0200 Subject: [PATCH 0216/2667] 5177 use dump() instead of print in examples --- components/class_loader/class_map_generator.rst | 2 +- components/css_selector.rst | 2 +- components/dom_crawler.rst | 2 +- components/finder.rst | 16 +++++++--------- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/components/class_loader/class_map_generator.rst b/components/class_loader/class_map_generator.rst index faf06e88809..b35aa731824 100644 --- a/components/class_loader/class_map_generator.rst +++ b/components/class_loader/class_map_generator.rst @@ -50,7 +50,7 @@ method:: use Symfony\Component\ClassLoader\ClassMapGenerator; - print_r(ClassMapGenerator::createMap(__DIR__.'/library')); + dump(ClassMapGenerator::createMap(__DIR__.'/library')); Given the files and class from the table above, you should see an output like this: diff --git a/components/css_selector.rst b/components/css_selector.rst index 833c8c04205..fb6c5cea7f9 100644 --- a/components/css_selector.rst +++ b/components/css_selector.rst @@ -51,7 +51,7 @@ equivalents:: use Symfony\Component\CssSelector\CssSelector; - print CssSelector::toXPath('div.item > h4 > a'); + dump(CssSelector::toXPath('div.item > h4 > a')); This gives the following output: diff --git a/components/dom_crawler.rst b/components/dom_crawler.rst index 0ef224d9a9c..b6609ecb192 100644 --- a/components/dom_crawler.rst +++ b/components/dom_crawler.rst @@ -47,7 +47,7 @@ traverse easily:: $crawler = new Crawler($html); foreach ($crawler as $domElement) { - print $domElement->nodeName; + dump($domElement->nodeName); } Specialized :class:`Symfony\\Component\\DomCrawler\\Link` and diff --git a/components/finder.rst b/components/finder.rst index 972f5ae7bcb..4d73e8a4ecb 100644 --- a/components/finder.rst +++ b/components/finder.rst @@ -30,14 +30,14 @@ directories:: $finder->files()->in(__DIR__); foreach ($finder as $file) { - // Print the absolute path - print $file->getRealpath()."\n"; + // Dump the absolute path + dump($file->getRealpath()); - // Print the relative path to the file, omitting the filename - print $file->getRelativePath()."\n"; + // Dump the relative path to the file, omitting the filename + dump($file->getRelativePath()); - // Print the relative path to the file - print $file->getRelativePathname()."\n"; + // Dump the relative path to the file + dump($file->getRelativePathname()); } The ``$file`` is an instance of :class:`Symfony\\Component\\Finder\\SplFileInfo` @@ -121,9 +121,7 @@ And it also works with user-defined streams:: $finder = new Finder(); $finder->name('photos*')->size('< 100K')->date('since 1 hour ago'); foreach ($finder->in('s3://bucket-name') as $file) { - // ... do something - - print $file->getFilename()."\n"; + // ... do something with the file } .. note:: From c2acbeee95cee4d34ff4d631ca5c387fbaa2bf49 Mon Sep 17 00:00:00 2001 From: Henry Snoek Date: Thu, 11 Jun 2015 20:25:13 +0200 Subject: [PATCH 0217/2667] 5177 add die() after dump() --- book/doctrine.rst | 1 + book/translation.rst | 2 ++ cookbook/bundles/remove.rst | 1 + 3 files changed, 4 insertions(+) diff --git a/book/doctrine.rst b/book/doctrine.rst index 4edc451946e..8b5a1d55567 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -1205,6 +1205,7 @@ to the given ``Category`` object via their ``category_id`` value. // prints "Proxies\AppBundleEntityCategoryProxy" dump(get_class($category)); + die(); This proxy object extends the true ``Category`` object, and looks and acts exactly like it. The difference is that, by using a proxy object, diff --git a/book/translation.rst b/book/translation.rst index f3be3a70e16..3d2f31b7993 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -13,10 +13,12 @@ into the language of the user:: // text will *always* print out in English dump('Hello World'); + die(); // text can be translated into the end-user's language or // default to English dump($translator->trans('Hello World')); + die(); .. note:: diff --git a/cookbook/bundles/remove.rst b/cookbook/bundles/remove.rst index 2cc87decf61..fbf186a388e 100644 --- a/cookbook/bundles/remove.rst +++ b/cookbook/bundles/remove.rst @@ -80,6 +80,7 @@ can remove the ``Acme`` directory as well. to get the path of the bundle:: dump($this->container->get('kernel')->getBundle('AcmeDemoBundle')->getPath()); + die(); 3.1 Remove Bundle Assets ~~~~~~~~~~~~~~~~~~~~~~~~ From 9fa1c11d1df41e65335e8ed772095ba45e8ceff5 Mon Sep 17 00:00:00 2001 From: Henry Snoek Date: Wed, 17 Jun 2015 20:07:31 +0200 Subject: [PATCH 0218/2667] 5177 use var_dump() in components Conflicts: components/expression_language/caching.rst components/expression_language/extending.rst components/expression_language/introduction.rst components/expression_language/syntax.rst components/serializer.rst --- .../class_loader/class_map_generator.rst | 2 +- components/css_selector.rst | 2 +- components/dom_crawler.rst | 2 +- components/event_dispatcher/generic_event.rst | 4 +-- components/finder.rst | 6 ++-- components/form/introduction.rst | 2 +- components/http_foundation/introduction.rst | 4 +-- components/intl.rst | 10 +++--- components/property_access/introduction.rst | 32 +++++++++---------- components/security/authorization.rst | 2 +- components/translation/custom_formats.rst | 2 +- components/translation/usage.rst | 6 ++-- 12 files changed, 37 insertions(+), 37 deletions(-) diff --git a/components/class_loader/class_map_generator.rst b/components/class_loader/class_map_generator.rst index b35aa731824..772bd9ab693 100644 --- a/components/class_loader/class_map_generator.rst +++ b/components/class_loader/class_map_generator.rst @@ -50,7 +50,7 @@ method:: use Symfony\Component\ClassLoader\ClassMapGenerator; - dump(ClassMapGenerator::createMap(__DIR__.'/library')); + var_dump(ClassMapGenerator::createMap(__DIR__.'/library')); Given the files and class from the table above, you should see an output like this: diff --git a/components/css_selector.rst b/components/css_selector.rst index fb6c5cea7f9..cbb2e133044 100644 --- a/components/css_selector.rst +++ b/components/css_selector.rst @@ -51,7 +51,7 @@ equivalents:: use Symfony\Component\CssSelector\CssSelector; - dump(CssSelector::toXPath('div.item > h4 > a')); + var_dump(CssSelector::toXPath('div.item > h4 > a')); This gives the following output: diff --git a/components/dom_crawler.rst b/components/dom_crawler.rst index b6609ecb192..fb82e1ae1a1 100644 --- a/components/dom_crawler.rst +++ b/components/dom_crawler.rst @@ -47,7 +47,7 @@ traverse easily:: $crawler = new Crawler($html); foreach ($crawler as $domElement) { - dump($domElement->nodeName); + var_dump($domElement->nodeName); } Specialized :class:`Symfony\\Component\\DomCrawler\\Link` and diff --git a/components/event_dispatcher/generic_event.rst b/components/event_dispatcher/generic_event.rst index 73ade2a1c8e..27d3723e994 100644 --- a/components/event_dispatcher/generic_event.rst +++ b/components/event_dispatcher/generic_event.rst @@ -75,7 +75,7 @@ the event arguments:: ); $dispatcher->dispatch('foo', $event); - dump($event['counter']); + var_dump($event['counter']); class FooListener { @@ -96,7 +96,7 @@ Filtering data:: $event = new GenericEvent($subject, array('data' => 'Foo')); $dispatcher->dispatch('foo', $event); - dump($event['data']); + var_dump($event['data']); class FooListener { diff --git a/components/finder.rst b/components/finder.rst index 4d73e8a4ecb..f4c1bde02dd 100644 --- a/components/finder.rst +++ b/components/finder.rst @@ -31,13 +31,13 @@ directories:: foreach ($finder as $file) { // Dump the absolute path - dump($file->getRealpath()); + var_dump($file->getRealpath()); // Dump the relative path to the file, omitting the filename - dump($file->getRelativePath()); + var_dump($file->getRelativePath()); // Dump the relative path to the file - dump($file->getRelativePathname()); + var_dump($file->getRelativePathname()); } The ``$file`` is an instance of :class:`Symfony\\Component\\Finder\\SplFileInfo` diff --git a/components/form/introduction.rst b/components/form/introduction.rst index 931dc87fdb5..89d1848fbc6 100644 --- a/components/form/introduction.rst +++ b/components/form/introduction.rst @@ -396,7 +396,7 @@ is created from the form factory. ->add('dueDate', 'date') ->getForm(); - dump($twig->render('new.html.twig', array( + var_dump($twig->render('new.html.twig', array( 'form' => $form->createView(), ))); diff --git a/components/http_foundation/introduction.rst b/components/http_foundation/introduction.rst index 2640459e8c9..287a78ac625 100644 --- a/components/http_foundation/introduction.rst +++ b/components/http_foundation/introduction.rst @@ -415,10 +415,10 @@ represented by a PHP callable instead of a string:: $response = new StreamedResponse(); $response->setCallback(function () { - dump('Hello World'); + var_dump('Hello World'); flush(); sleep(2); - dump('Hello World'); + var_dump('Hello World'); flush(); }); $response->send(); diff --git a/components/intl.rst b/components/intl.rst index 1a8dc20573e..75c843f5ee6 100644 --- a/components/intl.rst +++ b/components/intl.rst @@ -217,7 +217,7 @@ This class currently only works with the `intl extension`_ installed:: $reader = new BinaryBundleReader(); $data = $reader->read('/path/to/bundle', 'en'); - dump($data['Data']['entry1']); + var_dump($data['Data']['entry1']); PhpBundleReader ~~~~~~~~~~~~~~~ @@ -231,7 +231,7 @@ object:: $reader = new PhpBundleReader(); $data = $reader->read('/path/to/bundle', 'en'); - dump($data['Data']['entry1']); + var_dump($data['Data']['entry1']); BufferedBundleReader ~~~~~~~~~~~~~~~~~~~~ @@ -272,10 +272,10 @@ returned:: $data = $reader->read('/path/to/bundle', 'en'); // Produces an error if the key "Data" does not exist - dump($data['Data']['entry1']); + var_dump($data['Data']['entry1']); // Returns null if the key "Data" does not exist - dump($reader->readEntry('/path/to/bundle', 'en', array('Data', 'entry1'))); + var_dump($reader->readEntry('/path/to/bundle', 'en', array('Data', 'entry1'))); Additionally, the :method:`Symfony\\Component\\Intl\\ResourceBundle\\Reader\\StructuredBundleReaderInterface::readEntry` @@ -286,7 +286,7 @@ multi-valued entries (arrays), the values of the more specific and the fallback locale will be merged. In order to suppress this behavior, the last parameter ``$fallback`` can be set to ``false``:: - dump($reader->readEntry( + var_dump($reader->readEntry( '/path/to/bundle', 'en', array('Data', 'entry1'), diff --git a/components/property_access/introduction.rst b/components/property_access/introduction.rst index 1b1f5179b75..3df0b1b5a28 100644 --- a/components/property_access/introduction.rst +++ b/components/property_access/introduction.rst @@ -51,8 +51,8 @@ method. This is done using the index notation that is used in PHP:: 'first_name' => 'Wouter', ); - dump($accessor->getValue($person, '[first_name]')); // 'Wouter' - dump($accessor->getValue($person, '[age]')); // null + var_dump($accessor->getValue($person, '[first_name]')); // 'Wouter' + var_dump($accessor->getValue($person, '[age]')); // null As you can see, the method will return ``null`` if the index does not exists. @@ -86,13 +86,13 @@ To read from properties, use the "dot" notation:: $person = new Person(); $person->firstName = 'Wouter'; - dump($accessor->getValue($person, 'firstName')); // 'Wouter' + var_dump($accessor->getValue($person, 'firstName')); // 'Wouter' $child = new Person(); $child->firstName = 'Bar'; $person->children = array($child); - dump($accessor->getValue($person, 'children[0].firstName')); // 'Bar' + var_dump($accessor->getValue($person, 'children[0].firstName')); // 'Bar' .. caution:: @@ -122,7 +122,7 @@ property name (``first_name`` becomes ``FirstName``) and prefixes it with $person = new Person(); - dump($accessor->getValue($person, 'first_name')); // 'Wouter' + var_dump($accessor->getValue($person, 'first_name')); // 'Wouter' Using Hassers/Issers ~~~~~~~~~~~~~~~~~~~~ @@ -151,10 +151,10 @@ getters, this means that you can do something like this:: $person = new Person(); if ($accessor->getValue($person, 'author')) { - dump('He is an author'); + var_dump('He is an author'); } if ($accessor->getValue($person, 'children')) { - dump('He has children'); + var_dump('He has children'); } This will produce: ``He is an author`` @@ -179,7 +179,7 @@ The ``getValue`` method can also use the magic ``__get`` method:: $person = new Person(); - dump($accessor->getValue($person, 'Wouter')); // array(...) + var_dump($accessor->getValue($person, 'Wouter')); // array(...) Magic ``__call()`` Method ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -215,7 +215,7 @@ enable this feature by using :class:`Symfony\\Component\\PropertyAccess\\Propert ->enableMagicCall() ->getPropertyAccessor(); - dump($accessor->getValue($person, 'wouter')); // array(...) + var_dump($accessor->getValue($person, 'wouter')); // array(...) .. versionadded:: 2.3 The use of magic ``__call()`` method was introduced in Symfony 2.3. @@ -239,9 +239,9 @@ method:: $accessor->setValue($person, '[first_name]', 'Wouter'); - dump($accessor->getValue($person, '[first_name]')); // 'Wouter' + var_dump($accessor->getValue($person, '[first_name]')); // 'Wouter' // or - // dump($person['first_name']); // 'Wouter' + // var_dump($person['first_name']); // 'Wouter' Writing to Objects ------------------ @@ -275,9 +275,9 @@ can use setters, the magic ``__set`` method or properties to set values:: $accessor->setValue($person, 'lastName', 'de Jong'); $accessor->setValue($person, 'children', array(new Person())); - dump($person->firstName); // 'Wouter' - dump($person->getLastName()); // 'de Jong' - dump($person->children); // array(Person()); + var_dump($person->firstName); // 'Wouter' + var_dump($person->getLastName()); // 'de Jong' + var_dump($person->children); // array(Person()); You can also use ``__call`` to set values but you need to enable the feature, see `Enable other Features`_. @@ -313,7 +313,7 @@ see `Enable other Features`_. $accessor->setValue($person, 'wouter', array(...)); - dump($person->getWouter()); // array(...) + var_dump($person->getWouter()); // array(...) Mixing Objects and Arrays ------------------------- @@ -345,7 +345,7 @@ You can also mix objects and arrays:: $accessor->setValue($person, 'children[0].firstName', 'Wouter'); // equal to $person->getChildren()[0]->firstName = 'Wouter' - dump('Hello '.$accessor->getValue($person, 'children[0].firstName')); // 'Wouter' + var_dump('Hello '.$accessor->getValue($person, 'children[0].firstName')); // 'Wouter' // equal to $person->getChildren()[0]->firstName Enable other Features diff --git a/components/security/authorization.rst b/components/security/authorization.rst index 5032b91340c..3b170d106e4 100644 --- a/components/security/authorization.rst +++ b/components/security/authorization.rst @@ -187,7 +187,7 @@ first constructor argument:: $role = new Role('ROLE_ADMIN'); // will show 'ROLE_ADMIN' - dump($role->getRole()); + var_dump($role->getRole()); .. note:: diff --git a/components/translation/custom_formats.rst b/components/translation/custom_formats.rst index 793d118c549..14c6e65c8b0 100644 --- a/components/translation/custom_formats.rst +++ b/components/translation/custom_formats.rst @@ -63,7 +63,7 @@ Once created, it can be used as any other loader:: $translator->addResource('my_format', __DIR__.'/translations/messages.txt', 'fr_FR'); - dump($translator->trans('welcome')); + var_dump($translator->trans('welcome')); It will print *"accueil"*. diff --git a/components/translation/usage.rst b/components/translation/usage.rst index 55cb44ae832..eb0fe4978c5 100644 --- a/components/translation/usage.rst +++ b/components/translation/usage.rst @@ -15,7 +15,7 @@ Imagine you want to translate the string *"Symfony is great"* into French:: 'Symfony is great!' => 'J\'aime Symfony!', ), 'fr_FR'); - dump($translator->trans('Symfony is great!')); + var_dump($translator->trans('Symfony is great!')); In this example, the message *"Symfony is great!"* will be translated into the locale set in the constructor (``fr_FR``) if the message exists in one of @@ -31,7 +31,7 @@ Sometimes, a message containing a variable needs to be translated:: // ... $translated = $translator->trans('Hello '.$name); - dump($translated); + var_dump($translated); However, creating a translation for this string is impossible since the translator will try to look up the exact message, including the variable portions @@ -45,7 +45,7 @@ variable with a "placeholder":: array('%name%' => $name) ); - dump($translated); + var_dump($translated); Symfony will now look for a translation of the raw message (``Hello %name%``) and *then* replace the placeholders with their values. Creating a translation From 37db975273b091f717920675f16030ce8eb5c43c Mon Sep 17 00:00:00 2001 From: Henry Snoek Date: Wed, 17 Jun 2015 20:11:02 +0200 Subject: [PATCH 0219/2667] 5177 use var_dump() in components --- components/property_access/introduction.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/property_access/introduction.rst b/components/property_access/introduction.rst index 3df0b1b5a28..d5df21211b1 100644 --- a/components/property_access/introduction.rst +++ b/components/property_access/introduction.rst @@ -68,8 +68,8 @@ You can also use multi dimensional arrays:: ) ); - dump($accessor->getValue($persons, '[0][first_name]')); // 'Wouter' - dump($accessor->getValue($persons, '[1][first_name]')); // 'Ryan' + var_dump($accessor->getValue($persons, '[0][first_name]')); // 'Wouter' + var_dump($accessor->getValue($persons, '[1][first_name]')); // 'Ryan' Reading from Objects -------------------- From 00a522bf6309759de13665f651460b54170773d0 Mon Sep 17 00:00:00 2001 From: Henry Snoek Date: Sat, 23 May 2015 11:09:11 +0200 Subject: [PATCH 0220/2667] 5177 use dump() instead of echo in examples Conflicts: book/doctrine.rst components/event_dispatcher/introduction.rst components/serializer.rst --- components/expression_language/caching.rst | 4 ++-- components/expression_language/extending.rst | 3 ++- .../expression_language/introduction.rst | 8 +++---- components/expression_language/syntax.rst | 24 +++++++++---------- components/serializer.rst | 2 +- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/components/expression_language/caching.rst b/components/expression_language/caching.rst index f7de6fb9066..7b36ce4163d 100644 --- a/components/expression_language/caching.rst +++ b/components/expression_language/caching.rst @@ -56,7 +56,7 @@ Both ``evaluate()`` and ``compile()`` can handle ``ParsedExpression`` and // the parse() method returns a ParsedExpression $expression = $language->parse('1 + 4', array()); - echo $language->evaluate($expression); // prints 5 + dump($language->evaluate($expression)); // prints 5 .. code-block:: php @@ -68,7 +68,7 @@ Both ``evaluate()`` and ``compile()`` can handle ``ParsedExpression`` and serialize($language->parse('1 + 4', array())) ); - echo $language->evaluate($expression); // prints 5 + dump($language->evaluate($expression)); // prints 5 .. _DoctrineBridge: https://github.com/symfony/DoctrineBridge .. _`doctrine cache library`: http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/caching.html diff --git a/components/expression_language/extending.rst b/components/expression_language/extending.rst index 4aded7c5aeb..16708a2a5a8 100644 --- a/components/expression_language/extending.rst +++ b/components/expression_language/extending.rst @@ -44,7 +44,7 @@ This method has 3 arguments: return strtolower($str); }); - echo $language->evaluate('lowercase("HELLO")'); + dump($language->evaluate('lowercase("HELLO")')); This will print ``hello``. Both the **compiler** and **evaluator** are passed an ``arguments`` variable as their first argument, which is equal to the @@ -126,3 +126,4 @@ or by using the second argument of the constructor:: parent::__construct($parser, $providers); } } + diff --git a/components/expression_language/introduction.rst b/components/expression_language/introduction.rst index b14e37d5504..7ed8c179639 100644 --- a/components/expression_language/introduction.rst +++ b/components/expression_language/introduction.rst @@ -68,9 +68,9 @@ The main class of the component is $language = new ExpressionLanguage(); - echo $language->evaluate('1 + 2'); // displays 3 + dump($language->evaluate('1 + 2')); // displays 3 - echo $language->compile('1 + 2'); // displays (1 + 2) + dump($language->compile('1 + 2')); // displays (1 + 2) Expression Syntax ----------------- @@ -96,12 +96,12 @@ PHP type (including objects):: $apple = new Apple(); $apple->variety = 'Honeycrisp'; - echo $language->evaluate( + dump($language->evaluate( 'fruit.variety', array( 'fruit' => $apple, ) - ); + )); This will print "Honeycrisp". For more information, see the :doc:`/components/expression_language/syntax` entry, especially :ref:`component-expression-objects` and :ref:`component-expression-arrays`. diff --git a/components/expression_language/syntax.rst b/components/expression_language/syntax.rst index 56ec1da1173..7766ee075ec 100644 --- a/components/expression_language/syntax.rst +++ b/components/expression_language/syntax.rst @@ -42,12 +42,12 @@ to JavaScript:: $apple = new Apple(); $apple->variety = 'Honeycrisp'; - echo $language->evaluate( + dump($language->evaluate( 'fruit.variety', array( 'fruit' => $apple, ) - ); + )); This will print out ``Honeycrisp``. @@ -72,12 +72,12 @@ JavaScript:: $robot = new Robot(); - echo $language->evaluate( + dump($language->evaluate( 'robot.sayHi(3)', array( 'robot' => $robot, ) - ); + )); This will print out ``Hi Hi Hi!``. @@ -93,9 +93,9 @@ constant:: define('DB_USER', 'root'); - echo $language->evaluate( + dump($language->evaluate( 'constant("DB_USER")' - ); + )); This will print out ``root``. @@ -114,12 +114,12 @@ array keys, similar to JavaScript:: $data = array('life' => 10, 'universe' => 10, 'everything' => 22); - echo $language->evaluate( + dump($language->evaluate( 'data["life"] + data["universe"] + data["everything"]', array( 'data' => $data, ) - ); + )); This will print out ``42``. @@ -140,14 +140,14 @@ Arithmetic Operators For example:: - echo $language->evaluate( + dump($language->evaluate( 'life + universe + everything', array( 'life' => 10, 'universe' => 10, 'everything' => 22, ) - ); + )); This will print out ``42``. @@ -230,13 +230,13 @@ String Operators For example:: - echo $language->evaluate( + dump($language->evaluate( 'firstName~" "~lastName', array( 'firstName' => 'Arthur', 'lastName' => 'Dent', ) - ); + )); This would print out ``Arthur Dent``. diff --git a/components/serializer.rst b/components/serializer.rst index 30343fbdb32..d702bf5333e 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -360,7 +360,7 @@ having unique identifiers:: }); $serializer = new Serializer(array($normalizer), array($encoder)); - echo $serializer->serialize($org, 'json'); + dump($serializer->serialize($org, 'json')); // {"name":"Les-Tilleuls.coop","members":[{"name":"K\u00e9vin", organization: "Les-Tilleuls.coop"}]} JMSSerializer From ac4e02e037c93d5eeb9f526fc2bbab9bbe08a294 Mon Sep 17 00:00:00 2001 From: Henry Snoek Date: Wed, 17 Jun 2015 20:07:31 +0200 Subject: [PATCH 0221/2667] 5177 use var_dump() in components --- components/expression_language/caching.rst | 4 ++-- components/expression_language/extending.rst | 2 +- components/expression_language/introduction.rst | 6 +++--- components/expression_language/syntax.rst | 10 +++++----- components/serializer.rst | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/components/expression_language/caching.rst b/components/expression_language/caching.rst index 7b36ce4163d..4961d76bd6c 100644 --- a/components/expression_language/caching.rst +++ b/components/expression_language/caching.rst @@ -56,7 +56,7 @@ Both ``evaluate()`` and ``compile()`` can handle ``ParsedExpression`` and // the parse() method returns a ParsedExpression $expression = $language->parse('1 + 4', array()); - dump($language->evaluate($expression)); // prints 5 + var_dump($language->evaluate($expression)); // prints 5 .. code-block:: php @@ -68,7 +68,7 @@ Both ``evaluate()`` and ``compile()`` can handle ``ParsedExpression`` and serialize($language->parse('1 + 4', array())) ); - dump($language->evaluate($expression)); // prints 5 + var_dump($language->evaluate($expression)); // prints 5 .. _DoctrineBridge: https://github.com/symfony/DoctrineBridge .. _`doctrine cache library`: http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/caching.html diff --git a/components/expression_language/extending.rst b/components/expression_language/extending.rst index 16708a2a5a8..fa90165014d 100644 --- a/components/expression_language/extending.rst +++ b/components/expression_language/extending.rst @@ -44,7 +44,7 @@ This method has 3 arguments: return strtolower($str); }); - dump($language->evaluate('lowercase("HELLO")')); + var_dump($language->evaluate('lowercase("HELLO")')); This will print ``hello``. Both the **compiler** and **evaluator** are passed an ``arguments`` variable as their first argument, which is equal to the diff --git a/components/expression_language/introduction.rst b/components/expression_language/introduction.rst index 7ed8c179639..98b4402f7c7 100644 --- a/components/expression_language/introduction.rst +++ b/components/expression_language/introduction.rst @@ -68,9 +68,9 @@ The main class of the component is $language = new ExpressionLanguage(); - dump($language->evaluate('1 + 2')); // displays 3 + var_dump($language->evaluate('1 + 2')); // displays 3 - dump($language->compile('1 + 2')); // displays (1 + 2) + var_dump($language->compile('1 + 2')); // displays (1 + 2) Expression Syntax ----------------- @@ -96,7 +96,7 @@ PHP type (including objects):: $apple = new Apple(); $apple->variety = 'Honeycrisp'; - dump($language->eval 10000 uate( + var_dump($language->evaluate( 'fruit.variety', array( 'fruit' => $apple, diff --git a/components/expression_language/syntax.rst b/components/expression_language/syntax.rst index 7766ee075ec..338bd635e5a 100644 --- a/components/expression_language/syntax.rst +++ b/components/expression_language/syntax.rst @@ -42,7 +42,7 @@ to JavaScript:: $apple = new Apple(); $apple->variety = 'Honeycrisp'; - dump($language->evaluate( + var_dump($language->evaluate( 'fruit.variety', array( 'fruit' => $apple, @@ -72,7 +72,7 @@ JavaScript:: $robot = new Robot(); - dump($language->evaluate( + var_dump($language->evaluate( 'robot.sayHi(3)', array( 'robot' => $robot, @@ -93,7 +93,7 @@ constant:: define('DB_USER', 'root'); - dump($language->evaluate( + var_dump($language->evaluate( 'constant("DB_USER")' )); @@ -114,7 +114,7 @@ array keys, similar to JavaScript:: $data = array('life' => 10, 'universe' => 10, 'everything' => 22); - dump($language->evaluate( + var_dump($language->evaluate( 'data["life"] + data["universe"] + data["everything"]', array( 'data' => $data, @@ -230,7 +230,7 @@ String Operators For example:: - dump($language->evaluate( + var_dump($language->evaluate( 'firstName~" "~lastName', array( 'firstName' => 'Arthur', diff --git a/components/serializer.rst b/components/serializer.rst index d702bf5333e..33c9e6a19d2 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -360,7 +360,7 @@ having unique identifiers:: }); $serializer = new Serializer(array($normalizer), array($encoder)); - dump($serializer->serialize($org, 'json')); + var_dump($serializer->serialize($org, 'json')); // {"name":"Les-Tilleuls.coop","members":[{"name":"K\u00e9vin", organization: "Les-Tilleuls.coop"}]} JMSSerializer From 5c63064f16e0c7e1b58ba9c7029bca4e5bb2213b Mon Sep 17 00:00:00 2001 From: Henry Snoek Date: Mon, 22 Jun 2015 16:54:57 +0200 Subject: [PATCH 0222/2667] 5177 fix leftover dump() --- components/expression_language/syntax.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/expression_language/syntax.rst b/components/expression_language/syntax.rst index 338bd635e5a..ba153905aa6 100644 --- a/components/expression_language/syntax.rst +++ b/components/expression_language/syntax.rst @@ -140,7 +140,7 @@ Arithmetic Operators For example:: - dump($language->evaluate( + var_dump($language->evaluate( 'life + universe + everything', array( 'life' => 10, From acf66f9161ea02df8ab885db682dab6a5851f96b Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 27 Jun 2015 20:13:50 +0200 Subject: [PATCH 0223/2667] Move access decision strategy section --- cookbook/security/voters.rst | 83 -------------------- cookbook/security/voters_data_permission.rst | 82 +++++++++++++++++++ 2 files changed, 82 insertions(+), 83 deletions(-) diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index 4bc7df40a3a..4a3cd926d83 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -149,86 +149,3 @@ and tag it as a ``security.voter``: configuration file (e.g. ``app/config/config.yml``). For more information see :ref:`service-container-imports-directive`. To read more about defining services in general, see the :doc:`/book/service_container` chapter. - -.. _security-voters-change-strategy: - -Changing the Access Decision Strategy -------------------------------------- - -In order for the new voter to take effect, you need to change the default access -decision strategy, which, by default, grants access if *any* voter grants -access. - -In this case, choose the ``unanimous`` strategy. Unlike the ``affirmative`` -strategy (the default), with the ``unanimous`` strategy, if only one voter -denies access (e.g. the ``ClientIpVoter``), access is not granted to the -end user. - -To do that, override the default ``access_decision_manager`` section of your -application configuration file with the following code. - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - security: - access_decision_manager: - # strategy can be: affirmative, unanimous or consensus - strategy: unanimous - - .. code-block:: xml - - - - - - - - .. code-block:: php - - // app/config/security.xml - $container->loadFromExtension('security', array( - // strategy can be: affirmative, unanimous or consensus - 'access_decision_manager' => array( - 'strategy' => 'unanimous', - ), - )); - -That's it! Now, when deciding whether or not a user should have access, -the new voter will deny access to any user in the list of blacklisted IPs. - -Note that the voters are only called, if any access is actually checked. So -you need at least something like - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - security: - access_control: - - { path: ^/, role: IS_AUTHENTICATED_ANONYMOUSLY } - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - // app/config/security.xml - $container->loadFromExtension('security', array( - 'access_control' => array( - array('path' => '^/', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'), - ), - )); - -.. seealso:: - - For a more advanced usage see - :ref:`components-security-access-decision-manager`. diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 2376d393b53..240035c3289 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -220,3 +220,85 @@ from the security context is called. } It's that easy! + +.. _security-voters-change-strategy: + +Changing the Access Decision Strategy +------------------------------------- + +In order for the new voter to take effect, you need to change the default access +decision strategy, which, by default, grants access if *any* voter grants +access. + +In this case, choose the ``unanimous`` strategy. Unlike the ``affirmative`` +strategy (the default), with the ``unanimous`` strategy, if only one voter +denies access (e.g. the ``ClientIpVoter``), access is not granted to the +end user. + +To do that, override the default ``access_decision_manager`` section of your +application configuration file with the following code. + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + access_decision_manager: + # strategy can be: affirmative, unanimous or consensus + strategy: unanimous + + .. code-block:: xml + + + + + + + + .. code-block:: php + + // app/config/security.xml + $container->loadFromExtension('security', array( + // strategy can be: affirmative, unanimous or consensus + 'access_decision_manager' => array( + 'strategy' => 'unanimous', + ), + )); + +That's it! Now, when deciding whether or not a user should have access, +the new voter will deny access to any user in the list of blacklisted IPs. + +Note that the voters are only called, if any access is actually checked. So +you need at least something like + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + access_control: + - { path: ^/, role: IS_AUTHENTICATED_ANONYMOUSLY } + + .. code-block:: xml + + + + + + + + + .. code-block:: php + + // app/config/security.xml + $container->loadFromExtension('security', array( + 'access_control' => array( + array('path' => '^/', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'), + ), + )); + +.. seealso:: + + For a more advanced usage see :ref:`components-security-access-decision-manager`. From 9c169c79b96eba90735a7e31488a0223aa443676 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 27 Jun 2015 20:28:49 +0200 Subject: [PATCH 0224/2667] Rewrite new section --- cookbook/security/voters_data_permission.rst | 90 ++++++++------------ 1 file changed, 35 insertions(+), 55 deletions(-) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 240035c3289..3513b6dde0b 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -223,20 +223,30 @@ It's that easy! .. _security-voters-change-strategy: -Changing the Access Decision Strategy -------------------------------------- +Changing the Decision Strategy +------------------------------ -In order for the new voter to take effect, you need to change the default access -decision strategy, which, by default, grants access if *any* voter grants -access. +Imagine you have multiple voters for one action for an object. For instance, +you have one voter that checks if the user is a member of the site and a second +one checking if the user is older than 18. -In this case, choose the ``unanimous`` strategy. Unlike the ``affirmative`` -strategy (the default), with the ``unanimous`` strategy, if only one voter -denies access (e.g. the ``ClientIpVoter``), access is not granted to the -end user. +To handle these cases, the access decision manager uses a decision strategy. +You can configure this to suite your needs. There are three strategies +available: -To do that, override the default ``access_decision_manager`` section of your -application configuration file with the following code. +``affirmative`` (default) + This grants access as soon as there is *one* voter granting access; + +``consensus`` + This grants access if there are more voters granting access than denying; + +``unanimous`` + This only grants access once *all* voters grant access. + +In the above scenario, both voters should grant access in order to grant access +to the user to read the post. In this case, the default strategy is no longer +valid and ``unanimous`` should be used instead. You can set this in the +security configuration: .. configuration-block:: @@ -245,60 +255,30 @@ application configuration file with the following code. # app/config/security.yml security: access_decision_manager: - # strategy can be: affirmative, unanimous or consensus strategy: unanimous .. code-block:: xml - - - - + + + + + + .. code-block:: php - // app/config/security.xml + // app/config/security.php $container->loadFromExtension('security', array( - // strategy can be: affirmative, unanimous or consensus 'access_decision_manager' => array( 'strategy' => 'unanimous', ), )); - -That's it! Now, when deciding whether or not a user should have access, -the new voter will deny access to any user in the list of blacklisted IPs. - -Note that the voters are only called, if any access is actually checked. So -you need at least something like - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - security: - access_control: - - { path: ^/, role: IS_AUTHENTICATED_ANONYMOUSLY } - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - // app/config/security.xml - $container->loadFromExtension('security', array( - 'access_control' => array( - array('path' => '^/', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'), - ), - )); - -.. seealso:: - - For a more advanced usage see :ref:`components-security-access-decision-manager`. From d03c380917d84b5efaa400ee373ebaac3348909c Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 27 Jun 2015 21:21:25 +0200 Subject: [PATCH 0225/2667] Changed dump() to var_dump() --- book/doctrine.rst | 3 +-- book/translation.rst | 6 ++---- cookbook/bundles/remove.rst | 3 +-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/book/doctrine.rst b/book/doctrine.rst index 8b5a1d55567..63c15079822 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -1204,8 +1204,7 @@ to the given ``Category`` object via their ``category_id`` value. $category = $product->getCategory(); // prints "Proxies\AppBundleEntityCategoryProxy" - dump(get_class($category)); - die(); + var_dump(get_class($category)); This proxy object extends the true ``Category`` object, and looks and acts exactly like it. The difference is that, by using a proxy object, diff --git a/book/translation.rst b/book/translation.rst index 3d2f31b7993..e44c702bb9f 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -12,13 +12,11 @@ wrapping each with a function capable of translating the text (or "message") into the language of the user:: // text will *always* print out in English - dump('Hello World'); - die(); + var_dump('Hello World'); // text can be translated into the end-user's language or // default to English - dump($translator->trans('Hello World')); - die(); + var_dump($translator->trans('Hello World')); .. note:: diff --git a/cookbook/bundles/remove.rst b/cookbook/bundles/remove.rst index fbf186a388e..8dc6f5b4a6f 100644 --- a/cookbook/bundles/remove.rst +++ b/cookbook/bundles/remove.rst @@ -79,8 +79,7 @@ can remove the ``Acme`` directory as well. :method:`Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface::getPath` method to get the path of the bundle:: - dump($this->container->get('kernel')->getBundle('AcmeDemoBundle')->getPath()); - die(); + var_dump($this->container->get('kernel')->getBundle('AcmeDemoBundle')->getPath()); 3.1 Remove Bundle Assets ~~~~~~~~~~~~~~~~~~~~~~~~ From 58ca2097b41ce7951cfe9cce060fec8aaaab312f Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 27 Jun 2015 21:22:58 +0200 Subject: [PATCH 0226/2667] Revert "Changed dump() to var_dump()" This reverts commit d03c380917d84b5efaa400ee373ebaac3348909c. --- book/doctrine.rst | 3 ++- book/translation.rst | 6 ++++-- cookbook/bundles/remove.rst | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/book/doctrine.rst b/book/doctrine.rst index 10b8dbad5c7..f6b23b0b6f7 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -1204,7 +1204,8 @@ to the given ``Category`` object via their ``category_id`` value. $category = $product->getCategory(); // prints "Proxies\AppBundleEntityCategoryProxy" - var_dump(get_class($category)); + dump(get_class($category)); + die(); This proxy object extends the true ``Category`` object, and looks and acts exactly like it. The difference is that, by using a proxy object, diff --git a/book/translation.rst b/book/translation.rst index 0f4c009a310..92abb54cb82 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -12,11 +12,13 @@ wrapping each with a function capable of translating the text (or "message") into the language of the user:: // text will *always* print out in English - var_dump('Hello World'); + dump('Hello World'); + die(); // text can be translated into the end-user's language or // default to English - var_dump($translator->trans('Hello World')); + dump($translator->trans('Hello World')); + die(); .. note:: diff --git a/cookbook/bundles/remove.rst b/cookbook/bundles/remove.rst index 8dc6f5b4a6f..fbf186a388e 100644 --- a/cookbook/bundles/remove.rst +++ b/cookbook/bundles/remove.rst @@ -79,7 +79,8 @@ can remove the ``Acme`` directory as well. :method:`Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface::getPath` method to get the path of the bundle:: - var_dump($this->container->get('kernel')->getBundle('AcmeDemoBundle')->getPath()); + dump($this->container->get('kernel')->getBundle('AcmeDemoBundle')->getPath()); + die(); 3.1 Remove Bundle Assets ~~~~~~~~~~~~~~~~~~~~~~~~ From 93484a707df22b92ed3e6ca478e7ce996b863284 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 27 Jun 2015 20:39:20 +0200 Subject: [PATCH 0227/2667] Remove the old voter article --- book/security.rst | 10 +- cookbook/map.rst.inc | 1 - cookbook/security/acl.rst | 2 +- cookbook/security/index.rst | 1 - cookbook/security/voter_interface.rst.inc | 24 -- cookbook/security/voters.rst | 332 ++++++++++++++----- cookbook/security/voters_data_permission.rst | 284 ---------------- redirection_map | 1 + 8 files changed, 248 insertions(+), 407 deletions(-) delete mode 100644 cookbook/security/voter_interface.rst.inc delete mode 100644 cookbook/security/voters_data_permission.rst diff --git a/book/security.rst b/book/security.rst index 9bfe3c2809a..640152cfda2 100644 --- a/book/security.rst +++ b/book/security.rst @@ -929,10 +929,10 @@ other users. Also, as the admin user, you yourself want to be able to edit To accomplish this you have 2 options: -* :doc:`Voters ` allow you to - use business logic (e.g. the user can edit this post because they were - the creator) to determine access. You'll probably want this option - it's - flexible enough to solve the above situation. +* :doc:`Voters ` allow you to use business logic + (e.g. the user can edit this post because they were the creator) to determine + access. You'll probably want this option - it's flexible enough to solve the + above situation. * :doc:`ACLs ` allow you to create a database structure where you can assign *any* arbitrary user *any* access (e.g. EDIT, VIEW) @@ -1281,7 +1281,7 @@ Learn More from the Cookbook * :doc:`Forcing HTTP/HTTPS ` * :doc:`Impersonating a User ` -* :doc:`/cookbook/security/voters_data_permission` +* :doc:`/cookbook/security/voters` * :doc:`Access Control Lists (ACLs) ` * :doc:`/cookbook/security/remember_me` * :doc:`/cookbook/security/multiple_user_providers` diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 6c37de6d5f7..135342503b7 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -151,7 +151,6 @@ * :doc:`/cookbook/security/remember_me` * :doc:`/cookbook/security/impersonating_user` * :doc:`/cookbook/security/voters` - * :doc:`/cookbook/security/voters_data_permission` * :doc:`/cookbook/security/acl` * :doc:`/cookbook/security/acl_advanced` * :doc:`/cookbook/security/force_https` diff --git a/cookbook/security/acl.rst b/cookbook/security/acl.rst index c6313167c40..507efe00dd7 100644 --- a/cookbook/security/acl.rst +++ b/cookbook/security/acl.rst @@ -14,7 +14,7 @@ the ACL system comes in. Using ACL's isn't trivial, and for simpler use cases, it may be overkill. If your permission logic could be described by just writing some code (e.g. to check if a Blog is owned by the current User), then consider using - :doc:`voters `. A voter is passed the object + :doc:`voters `. A voter is passed the object being voted on, which you can use to make complex decisions and effectively implement your own ACL. Enforcing authorization (e.g. the ``isGranted`` part) will look similar to what you see in this entry, but your voter diff --git a/cookbook/security/index.rst b/cookbook/security/index.rst index 5bf643c10e8..e666a2dcff4 100644 --- a/cookbook/security/index.rst +++ b/cookbook/security/index.rst @@ -9,7 +9,6 @@ Security remember_me impersonating_user voters - voters_data_permission acl acl_advanced force_https diff --git a/cookbook/security/voter_interface.rst.inc b/cookbook/security/voter_interface.rst.inc deleted file mode 100644 index 1a3cd989e3c..00000000000 --- a/cookbook/security/voter_interface.rst.inc +++ /dev/null @@ -1,24 +0,0 @@ -.. code-block:: php - - interface VoterInterface - { - public function supportsAttribute($attribute); - public function supportsClass($class); - public function vote(TokenInterface $token, $object, array $attributes); - } - -The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsAttribute` -method is used to check if the voter supports the given user attribute (i.e: -a role like ``ROLE_USER``, an ACL ``EDIT``, etc.). - -The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsClass` -method is used to check if the voter supports the class of the object whose -access is being checked. - -The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::vote` -method must implement the business logic that verifies whether or not the -user has access. This method must return one of the following values: - -* ``VoterInterface::ACCESS_GRANTED``: The authorization will be granted by this voter; -* ``VoterInterface::ACCESS_ABSTAIN``: The voter cannot decide if authorization should be granted; -* ``VoterInterface::ACCESS_DENIED``: The authorization will be denied by this voter. diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index 4a3cd926d83..7025d61ae3e 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -1,151 +1,301 @@ .. index:: - single: Security; Voters + single: Security; Data Permission + single: Security: Voters -How to Implement your own Voter to Blacklist IP Addresses -========================================================= +How to Use Voters to Check User Permissions +=========================================== -The Symfony Security component provides several layers to authorize users. -One of the layers is called a "voter". A voter is a dedicated class that checks -if the user has the rights to connect to the application or access a specific -resource/URL. For instance, Symfony provides a layer that checks if the user -is fully authorized or if it has some expected roles. +In Symfony, you can check the permission to access data by using the +:doc:`ACL module `, which is a bit overwhelming +for many applications. A much easier solution is to work with custom voters, +which are like simple conditional statements. -It is sometimes useful to create a custom voter to handle a specific case not -handled by the framework. In this section, you'll learn how to create a voter -that will allow you to blacklist users by their IP. +.. tip:: + + Take a look at the + :doc:`authorization ` + chapter for an even deeper understanding on voters. + +How Symfony Uses Voters +----------------------- + +In order to use voters, you have to understand how Symfony works with them. +All voters are called each time you use the ``isGranted()`` method on Symfony's +security context (i.e. the ``security.context`` service). Each one decides +if the current user should have access to some resource. + +Ultimately, Symfony uses one of three different approaches on what to do +with the feedback from all voters: affirmative, consensus and unanimous. + +For more information take a look at +:ref:`the section about access decision managers `. The Voter Interface ------------------- A custom voter must implement :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`, -which requires the following three methods: +which has this structure:: -.. include:: /cookbook/security/voter_interface.rst.inc + interface VoterInterface + { + public function supportsAttribute($attribute); + public function supportsClass($class); + public function vote(TokenInterface $token, $object, array $attributes); + } -In this example, you'll check if the user's IP address matches against a list of -blacklisted addresses and "something" will be the application. If the user's IP is blacklisted, you'll return -``VoterInterface::ACCESS_DENIED``, otherwise you'll return -``VoterInterface::ACCESS_ABSTAIN`` as this voter's purpose is only to deny -access, not to grant access. +The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsAttribute` +method is used to check if the voter supports the given user attribute (i.e: +a role like ``ROLE_USER``, an ACL ``EDIT``, etc.). -Creating a custom Voter ------------------------ +The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsClass` +method is used to check if the voter supports the class of the object whose +access is being checked. -To blacklist a user based on its IP, you can use the ``request`` service -and compare the IP address against a set of blacklisted IP addresses: +The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::vote` +method must implement the business logic that verifies whether or not the +user has access. This method must return one of the following values: -.. code-block:: php +* ``VoterInterface::ACCESS_GRANTED``: The authorization will be granted by this voter; +* ``VoterInterface::ACCESS_ABSTAIN``: The voter cannot decide if authorization should be granted; +* ``VoterInterface::ACCESS_DENIED``: The authorization will be denied by this voter. + +In this example, the voter will check if the user has access to a specific +object according to your custom conditions (e.g. they must be the owner of +the object). If the condition fails, you'll return +``VoterInterface::ACCESS_DENIED``, otherwise you'll return +``VoterInterface::ACCESS_GRANTED``. In case the responsibility for this decision +does not belong to this voter, it will return ``VoterInterface::ACCESS_ABSTAIN``. + +Creating the custom Voter +------------------------- - // src/AppBundle/Security/Authorization/Voter/ClientIpVoter.php +The goal is to create a voter that checks if a user has access to view or +edit a particular object. Here's an example implementation:: + + // src/AppBundle/Security/Authorization/Voter/PostVoter.php namespace AppBundle\Security\Authorization\Voter; - use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + use Symfony\Component\Security\Core\User\UserInterface; - class ClientIpVoter implements VoterInterface + class PostVoter implements VoterInterface { - private $container; - - private $blacklistedIp; - - public function __construct(ContainerInterface $container, array $blacklistedIp = array()) - { - $this->container = $container; - $this->blacklistedIp = $blacklistedIp; - } + const VIEW = 'view'; + const EDIT = 'edit'; public function supportsAttribute($attribute) { - // you won't check against a user attribute, so return true - return true; + return in_array($attribute, array( + self::VIEW, + self::EDIT, + )); } public function supportsClass($class) { - // your voter supports all type of token classes, so return true - return true; + $supportedClass = 'AppBundle\Entity\Post'; + + return $supportedClass === $class || is_subclass_of($class, $supportedClass); } - public function vote(TokenInterface $token, $object, array $attributes) + /** + * @var \AppBundle\Entity\Post $post + */ + public function vote(TokenInterface $token, $post, array $attributes) { - $request = $this->container->get('request'); - if (in_array($request->getClientIp(), $this->blacklistedIp)) { + // check if class of this object is supported by this voter + if (!$this->supportsClass(get_class($post))) { + return VoterInterface::ACCESS_ABSTAIN; + } + + // check if the voter is used correct, only allow one attribute + // this isn't a requirement, it's just one easy way for you to + // design your voter + if (1 !== count($attributes)) { + throw new \InvalidArgumentException( + 'Only one attribute is allowed for VIEW or EDIT' + ); + } + + // set the attribute to check against + $attribute = $attributes[0]; + + // check if the given attribute is covered by this voter + if (!$this->supportsAttribute($attribute)) { + return VoterInterface::ACCESS_ABSTAIN; + } + + // get current logged in user + $user = $token->getUser(); + + // make sure there is a user object (i.e. that the user is logged in) + if (!$user instanceof UserInterface) { return VoterInterface::ACCESS_DENIED; } - return VoterInterface::ACCESS_ABSTAIN; + switch($attribute) { + case self::VIEW: + // the data object could have for example a method isPrivate() + // which checks the boolean attribute $private + if (!$post->isPrivate()) { + return VoterInterface::ACCESS_GRANTED; + } + break; + + case self::EDIT: + // we assume that our data object has a method getOwner() to + // get the current owner user entity for this data object + if ($user->getId() === $post->getOwner()->getId()) { + return VoterInterface::ACCESS_GRANTED; + } + break; + } + + return VoterInterface::ACCESS_DENIED; } } That's it! The voter is done. The next step is to inject the voter into -the security layer. This can be done easily through the service container. - -.. tip:: - - Your implementation of the methods - :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsAttribute` - and :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsClass` - are not being called internally by the framework. Once you have registered your - voter the ``vote()`` method will always be called, regardless of whether - or not these two methods return true. Therefore you need to call those - methods in your implementation of the ``vote()`` method and return ``ACCESS_ABSTAIN`` - if your voter does not support the class or attribute. +the security layer. Declaring the Voter as a Service -------------------------------- -To inject the voter into the security layer, you must declare it as a service, -and tag it as a ``security.voter``: +To inject the voter into the security layer, you must declare it as a service +and tag it with ``security.voter``: .. configuration-block:: .. code-block:: yaml - # src/Acme/AcmeBundle/Resources/config/services.yml + # src/AppBundle/Resources/config/services.yml services: - security.access.blacklist_voter: - class: AppBundle\Security\Authorization\Voter\ClientIpVoter - arguments: ["@service_container", [123.123.123.123, 171.171.171.171]] - public: false + security.access.post_voter: + class: AppBundle\Security\Authorization\Voter\PostVoter + public: false tags: - - { name: security.voter } + - { name: security.voter } .. code-block:: xml - - - - - 123.123.123.123 - 171.171.171.171 - - - + + + + + + + + + .. code-block:: php - // src/Acme/AcmeBundle/Resources/config/services.php - use Symfony\Component\DependencyInjection\Definition; - use Symfony\Component\DependencyInjection\Reference; + // src/AppBundle/Resources/config/services.php + $container + ->register( + 'security.access.post_document_voter', + 'AppBundle\Security\Authorization\Voter\PostVoter' + ) + ->addTag('security.voter') + ; - $definition = new Definition( - 'AppBundle\Security\Authorization\Voter\ClientIpVoter', - array( - new Reference('service_container'), - array('123.123.123.123', '171.171.171.171'), - ), - ); - $definition->addTag('security.voter'); - $definition->setPublic(false); +How to Use the Voter in a Controller +------------------------------------ - $container->setDefinition('security.access.blacklist_voter', $definition); +The registered voter will then always be asked as soon as the method ``isGranted()`` +from the security context is called. -.. tip:: +.. code-block:: php + + // src/AppBundle/Controller/PostController.php + namespace AppBundle\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\Controller; + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\Security\Core\Exception\AccessDeniedException; + + class PostController extends Controller + { + public function showAction($id) + { + // get a Post instance + $post = ...; + + // keep in mind, this will call all registered security voters + if (false === $this->get('security.context')->isGranted('view', $post)) { + throw new AccessDeniedException('Unauthorised access!'); + } + + return new Response('

'.$post->getName().'

'); + } + } + +It's that easy! + +.. _security-voters-change-strategy: + +Changing the Access Decision Strategy +------------------------------------- + +Imagine you have multiple voters for one action for an object. For instance, +you have one voter that checks if the user is a member of the site and a second +one checking if the user is older than 18. - Be sure to import this configuration file from your main application - configuration file (e.g. ``app/config/config.yml``). For more information - see :ref:`service-container-imports-directive`. To read more about defining - services in general, see the :doc:`/book/service_container` chapter. +To handle these cases, the access decision manager uses an access decision +strategy. You can configure this to suite your needs. There are three +strategies available: + +``affirmative`` (default) + This grants access as soon as there is *one* voter granting access; + +``consensus`` + This grants access if there are more voters granting access than denying; + +``unanimous`` + This only grants access once *all* voters grant access. + +In the above scenario, both voters should grant access in order to grant access +to the user to read the post. In this case, the default strategy is no longer +valid and ``unanimous`` should be used instead. You can set this in the +security configuration: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + access_decision_manager: + strategy: unanimous + + .. code-block:: xml + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'access_decision_manager' => array( + 'strategy' => 'unanimous', + ), + )); diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst deleted file mode 100644 index 3513b6dde0b..00000000000 --- a/cookbook/security/voters_data_permission.rst +++ /dev/null @@ -1,284 +0,0 @@ -.. index:: - single: Security; Data Permission Voters - -How to Use Voters to Check User Permissions -=========================================== - -In Symfony, you can check the permission to access data by using the -:doc:`ACL module `, which is a bit overwhelming -for many applications. A much easier solution is to work with custom voters, -which are like simple conditional statements. - -.. seealso:: - - Voters can also be used in other ways, like, for example, blacklisting IP - addresses from the entire application: :doc:`/cookbook/security/voters`. - -.. tip:: - - Take a look at the - :doc:`authorization ` - chapter for an even deeper understanding on voters. - -How Symfony Uses Voters ------------------------ - -In order to use voters, you have to understand how Symfony works with them. -All voters are called each time you use the ``isGranted()`` method on Symfony's -security context (i.e. the ``security.context`` service). Each one decides -if the current user should have access to some resource. - -Ultimately, Symfony uses one of three different approaches on what to do -with the feedback from all voters: affirmative, consensus and unanimous. - -For more information take a look at -:ref:`the section about access decision managers `. - -The Voter Interface -------------------- - -A custom voter must implement -:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`, -which has this structure: - -.. include:: /cookbook/security/voter_interface.rst.inc - -In this example, the voter will check if the user has access to a specific -object according to your custom conditions (e.g. they must be the owner of -the object). If the condition fails, you'll return -``VoterInterface::ACCESS_DENIED``, otherwise you'll return -``VoterInterface::ACCESS_GRANTED``. In case the responsibility for this decision -does not belong to this voter, it will return ``VoterInterface::ACCESS_ABSTAIN``. - -Creating the custom Voter -------------------------- - -The goal is to create a voter that checks if a user has access to view or -edit a particular object. Here's an example implementation:: - - // src/AppBundle/Security/Authorization/Voter/PostVoter.php - namespace AppBundle\Security\Authorization\Voter; - - use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; - use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; - use Symfony\Component\Security\Core\User\UserInterface; - - class PostVoter implements VoterInterface - { - const VIEW = 'view'; - const EDIT = 'edit'; - - public function supportsAttribute($attribute) - { - return in_array($attribute, array( - self::VIEW, - self::EDIT, - )); - } - - public function supportsClass($class) - { - $supportedClass = 'AppBundle\Entity\Post'; - - return $supportedClass === $class || is_subclass_of($class, $supportedClass); - } - - /** - * @var \AppBundle\Entity\Post $post - */ - public function vote(TokenInterface $token, $post, array $attributes) - { - // check if class of this object is supported by this voter - if (!$this->supportsClass(get_class($post))) { - return VoterInterface::ACCESS_ABSTAIN; - } - - // check if the voter is used correct, only allow one attribute - // this isn't a requirement, it's just one easy way for you to - // design your voter - if (1 !== count($attributes)) { - throw new \InvalidArgumentException( - 'Only one attribute is allowed for VIEW or EDIT' - ); - } - - // set the attribute to check against - $attribute = $attributes[0]; - - // check if the given attribute is covered by this voter - if (!$this->supportsAttribute($attribute)) { - return VoterInterface::ACCESS_ABSTAIN; - } - - // get current logged in user - $user = $token->getUser(); - - // make sure there is a user object (i.e. that the user is logged in) - if (!$user instanceof UserInterface) { - return VoterInterface::ACCESS_DENIED; - } - - switch($attribute) { - case self::VIEW: - // the data object could have for example a method isPrivate() - // which checks the boolean attribute $private - if (!$post->isPrivate()) { - return VoterInterface::ACCESS_GRANTED; - } - break; - - case self::EDIT: - // we assume that our data object has a method getOwner() to - // get the current owner user entity for this data object - if ($user->getId() === $post->getOwner()->getId()) { - return VoterInterface::ACCESS_GRANTED; - } - break; - } - - return VoterInterface::ACCESS_DENIED; - } - } - -That's it! The voter is done. The next step is to inject the voter into -the security layer. - -Declaring the Voter as a Service --------------------------------- - -To inject the voter into the security layer, you must declare it as a service -and tag it with ``security.voter``: - -.. configuration-block:: - - .. code-block:: yaml - - # src/AppBundle/Resources/config/services.yml - services: - security.access.post_voter: - class: AppBundle\Security\Authorization\Voter\PostVoter - public: false - tags: - - { name: security.voter } - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // src/AppBundle/Resources/config/services.php - $container - ->register( - 'security.access.post_document_voter', - 'AppBundle\Security\Authorization\Voter\PostVoter' - ) - ->addTag('security.voter') - ; - -How to Use the Voter in a Controller ------------------------------------- - -The registered voter will then always be asked as soon as the method ``isGranted()`` -from the security context is called. - -.. code-block:: php - - // src/AppBundle/Controller/PostController.php - namespace AppBundle\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\Controller; - use Symfony\Component\HttpFoundation\Response; - use Symfony\Component\Security\Core\Exception\AccessDeniedException; - - class PostController extends Controller - { - public function showAction($id) - { - // get a Post instance - $post = ...; - - // keep in mind, this will call all registered security voters - if (false === $this->get('security.context')->isGranted('view', $post)) { - throw new AccessDeniedException('Unauthorised access!'); - } - - return new Response('

'.$post->getName().'

'); - } - } - -It's that easy! - -.. _security-voters-change-strategy: - -Changing the Decision Strategy ------------------------------- - -Imagine you have multiple voters for one action for an object. For instance, -you have one voter that checks if the user is a member of the site and a second -one checking if the user is older than 18. - -To handle these cases, the access decision manager uses a decision strategy. -You can configure this to suite your needs. There are three strategies -available: - -``affirmative`` (default) - This grants access as soon as there is *one* voter granting access; - -``consensus`` - This grants access if there are more voters granting access than denying; - -``unanimous`` - This only grants access once *all* voters grant access. - -In the above scenario, both voters should grant access in order to grant access -to the user to read the post. In this case, the default strategy is no longer -valid and ``unanimous`` should be used instead. You can set this in the -security configuration: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - security: - access_decision_manager: - strategy: unanimous - - .. code-block:: xml - - - - - - - - - - .. code-block:: php - - // app/config/security.php - $container->loadFromExtension('security', array( - 'access_decision_manager' => array( - 'strategy' => 'unanimous', - ), - )); diff --git a/redirection_map b/redirection_map index 7e60794f8c6..1ebc69ced38 100644 --- a/redirection_map +++ b/redirection_map @@ -25,3 +25,4 @@ /components/yaml /components/yaml/introduction /components/templating /components/templating/introduction /cookbook/upgrading /cookbook/upgrade/index +/cookbook/security/voters_data_permission /cookbook/security/voters From 64b90817fef3fe60364b093c0367a5029383c24e Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 28 Jun 2015 00:03:21 +0200 Subject: [PATCH 0228/2667] Improve travis build speed --- .travis.yml | 12 +++++++----- requirements.txt | 1 - 2 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 requirements.txt diff --git a/.travis.yml b/.travis.yml index 73bca138115..d9966ccf190 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,18 @@ language: python -python: - - "2.7" +python: "2.7" sudo: false -install: - - "pip install -q -r requirements.txt --use-mirrors" +cache: + directories: + - $HOME/.pip-cache/ + - _build + +install: pip install --download-cache $HOME/.pip-cache --user sphinx==1.1.3 script: sphinx-build -nW -b html -d _build/doctrees . _build/html branches: except: - github-comments - diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 3049177233e..00000000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -Sphinx==1.1.3 From bf18f16c82e74c8b8169adfe67b1b7634226bf1b Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 27 Jun 2015 18:35:52 -0400 Subject: [PATCH 0229/2667] [#5166] Minor tweaks --- cookbook/assetic/php.rst | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/cookbook/assetic/php.rst b/cookbook/assetic/php.rst index b4ce591079e..6133497f498 100644 --- a/cookbook/assetic/php.rst +++ b/cookbook/assetic/php.rst @@ -18,7 +18,7 @@ some scenarios: * If you want to simplify application deployment. In this article, you'll learn how to combine and minimize CSS and JavaScript files -and how to compile Sass files using PHP only libraries with Assetic. +and how to compile Sass files using PHP-only libraries with Assetic. Installing the Third-Party Compression Libraries ------------------------------------------------ @@ -39,10 +39,9 @@ dependency because the most recent stable version is not compatible with Assetic Organizing your Web Asset Files ------------------------------- -This example shows the common scenario of using the Bootstrap framework, the -jQuery library, the FontAwesome icon fonts and some regular CSS and JavaScript -application files (called ``main.css`` and ``main.js``). The recommended directory -structure for this set-up is the following: +This example will include a setup using the Bootstrap CSS framework, jQuery, FontAwesome +and some regular CSS and and JavaScript application files (called ``main.css`` and +``main.js``). The recommended directory structure for this set-up looks like this: .. code-block:: text @@ -74,7 +73,7 @@ structure for this set-up is the following: Combining and Minimizing CSS Files and Compiling SCSS Files ----------------------------------------------------------- -First, configure a new ``scssphp`` Assetic filter as follows: +First, configure a new ``scssphp`` Assetic filter: .. configuration-block:: @@ -114,11 +113,10 @@ First, configure a new ``scssphp`` Assetic filter as follows: The value of the ``formatter`` option is the fully qualified class name of the formatter used by the filter to produce the compiled CSS file. Using the -compressed formatter allows to minimize the resulting file, no matter if the -original files are regular CSS files or SCSS files. +compressed formatter will minimize the the resulting file, regardless of whether +the original files are regular CSS files or SCSS files. -Then, update the code of your Twig template to add the ``{% stylesheets %}`` tag -defined by Assetic: +Next, your Twig template to add the ``{% stylesheets %}`` tag defined by Assetic: .. code-block:: html+jinja @@ -178,7 +176,7 @@ First, configure a new ``jsqueeze`` Assetic filter as follows: ), )); -Then, update the code of your Twig template to add the ``{% javascripts %}`` tag +Next, update the code of your Twig template to add the ``{% javascripts %}`` tag defined by Assetic: .. code-block:: html+jinja @@ -200,6 +198,6 @@ This simple configuration combines all the JavaScript files, minimizes the conte and saves the output in the ``web/js/app.js`` file, which is the one that is served to your visitors. -The leading ``?`` character in the ``jsqueeze`` filter name indicates that it must -be applied only when the ``debug`` mode is disabled in the application, which -usually occurs in the production environment. +The leading ``?`` character in the ``jsqueeze`` filter name tells Assetic to only +apply the filter when *not* in ``debug`` mode. In practice, this means that you'll +see unminified files while developing and minimized files in the ``prod`` environment. From 875f4eebe3a1c8b0dfe3c44a576c21a5dcdb3710 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 27 Jun 2015 18:43:51 -0400 Subject: [PATCH 0230/2667] removing upgrading.rst - this has already been moved into several documents in its own section So, this is not really removing anything - it's just that this article was "moved" originally, in #5186, but then later on a rebase, it appeared that this upgrading.rst was "added". The result is the intention of the #5186 PR: to add a new "install" section with *just* the new "unstable" article. --- cookbook/install/index.rst | 1 - cookbook/install/upgrading.rst | 137 --------------------------------- cookbook/map.rst.inc | 1 - redirection_map | 1 - 4 files changed, 140 deletions(-) delete mode 100644 cookbook/install/upgrading.rst diff --git a/cookbook/install/index.rst b/cookbook/install/index.rst index 9177815770e..8ec509592f9 100644 --- a/cookbook/install/index.rst +++ b/cookbook/install/index.rst @@ -4,5 +4,4 @@ Install and Upgrade .. toctree:: :maxdepth: 2 - upgrading unstable_versions diff --git a/cookbook/install/upgrading.rst b/cookbook/install/upgrading.rst deleted file mode 100644 index 34d11369ce5..00000000000 --- a/cookbook/install/upgrading.rst +++ /dev/null @@ -1,137 +0,0 @@ -How to Upgrade Your Symfony Project -=================================== - -So a new Symfony release has come out and you want to upgrade, great! Fortunately, -because Symfony protects backwards-compatibility very closely, this *should* -be quite easy. - -There are two types of upgrades, and both are a little different: - -* :ref:`upgrading-patch-version` -* :ref:`upgrading-minor-version` - -.. _upgrading-patch-version: - -Upgrading a Patch Version (e.g. 2.6.0 to 2.6.1) ------------------------------------------------ - -If you're upgrading and only the patch version (the last number) is changing, -then it's *really* easy: - -.. code-block:: bash - - $ composer update symfony/symfony - -That's it! You should not encounter any backwards-compatibility breaks or -need to change anything else in your code. That's because when you started -your project, your ``composer.json`` included Symfony using a constraint -like ``2.6.*``, where only the *last* version number will change when you -update. - -You may also want to upgrade the rest of your libraries. If you've done a -good job with your `version constraints`_ in ``composer.json``, you can do -this safely by running: - -.. code-block:: bash - - $ composer update - -But beware. If you have some bad `version constraints`_ in your ``composer.json``, -(e.g. ``dev-master``), then this could upgrade some non-Symfony libraries -to new versions that contain backwards-compatibility breaking changes. - -.. _upgrading-minor-version: - -Upgrading a Minor Version (e.g. 2.5.3 to 2.6.1) ------------------------------------------------ - -If you're upgrading a minor version (where the middle number changes), then -you should also *not* encounter significant backwards compatibility changes. -For details, see our :doc:`/contributing/code/bc`. - -However, some backwards-compatibility breaks *are* possible, and you'll learn -in a second how to prepare for them. - -There are two steps to upgrading: - -:ref:`upgrade-minor-symfony-composer`; -:ref:`upgrade-minor-symfony-code` - -1) Update the Symfony Library via Composer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -First, you need to update Symfony by modifying your ``composer.json`` file -to use the new version: - -.. code-block:: json - - { - "...": "...", - - "require": { - "php": ">=5.3.3", - "symfony/symfony": "2.6.*", - "...": "... no changes to anything else..." - }, - "...": "...", - } - -Next, use Composer to download new versions of the libraries: - -.. code-block:: bash - - $ composer update symfony/symfony - -You may also want to upgrade the rest of your libraries. If you've done a -good job with your `version constraints`_ in ``composer.json``, you can do -this safely by running: - -.. code-block:: bash - - $ composer update - -But beware. If you have some bad `version constraints`_ in your ``composer.json``, -(e.g. ``dev-master``), then this could upgrade some non-Symfony libraries -to new versions that contain backwards-compatibility breaking changes. - -2) Updating Your Code to Work with the new Version -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In theory, you should be done! However, you *may* need to make a few changes -to your code to get everything working. Additionally, some features you're -using might still work, but might now be deprecated. That's actually ok, -but if you know about these deprecations, you can start to fix them over -time. - -Every version of Symfony comes with an UPGRADE file that describes these -changes. Below are links to the file for each version, which you'll need -to read to see if you need any code changes. - -.. tip:: - - Don't see the version here that you're upgrading to? Just find the - UPGRADE-X.X.md file for the appropriate version on the `Symfony Repository`_. - -Upgrading to Symfony 2.6 -........................ - -First, of course, update your ``composer.json`` file with the ``2.6`` version -of Symfony as described above in :ref:`upgrade-minor-symfony-composer`. - -Next, check the `UPGRADE-2.6`_ document for details about any code changes -that you might need to make in your project. - -Upgrading to Symfony 2.5 -........................ - -First, of course, update your ``composer.json`` file with the ``2.5`` version -of Symfony as described above in :ref:`upgrade-minor-symfony-composer`. - -Next, check the `UPGRADE-2.5`_ document for details about any code changes -that you might need to make in your project. - -.. _`UPGRADE-2.5`: https://github.com/symfony/symfony/blob/2.5/UPGRADE-2.5.md -.. _`UPGRADE-2.6`: https://github.com/symfony/symfony/blob/2.6/UPGRADE-2.6.md -.. _`Symfony Repository`: https://github.com/symfony/symfony -.. _`Composer Package Versions`: https://getcomposer.org/doc/01-basic-usage.md#package-versions -.. _`version constraints`: https://getcomposer.org/doc/01-basic-usage.md#package-versions diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 8a7e0f80c27..8caed945d0c 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -116,7 +116,6 @@ * :doc:`/cookbook/install/index` - * :doc:`/cookbook/install/upgrading` * :doc:`/cookbook/install/unstable_versions` * :doc:`/cookbook/logging/index` diff --git a/redirection_map b/redirection_map index b0cb87b38ad..7e60794f8c6 100644 --- a/redirection_map +++ b/redirection_map @@ -14,7 +14,6 @@ /cookbook/service_container/parentservices /components/dependency_injection/parentservices /cookbook/service_container/factories /components/dependency_injection/factories /cookbook/service_container/tags /components/dependency_injection/tags -/cookbook/upgrading /cookbook/install/upgrading /reference/configuration/mongodb /bundles/DoctrineMongoDBBundle/config /reference/YAML /components/yaml /components/dependency_injection /components/dependency_injection/introduction From 037eda894de80900bff65c27d1b318484f76baaa Mon Sep 17 00:00:00 2001 From: Patrick McAndrew Date: Fri, 8 May 2015 14:09:35 +0100 Subject: [PATCH 0231/2667] Update load_balancer_reverse_proxy.rst Remind that the trusted_proxies setting needs to be removed or the setTrustedProxies method call will be overwritten. --- cookbook/request/load_balancer_reverse_proxy.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cookbook/request/load_balancer_reverse_proxy.rst b/cookbook/request/load_balancer_reverse_proxy.rst index e23e0ac01c2..650d9bf7421 100644 --- a/cookbook/request/load_balancer_reverse_proxy.rst +++ b/cookbook/request/load_balancer_reverse_proxy.rst @@ -83,6 +83,9 @@ In this case, you'll need to - *very carefully* - trust *all* proxies. $response = $kernel->handle($request); // ... +#. Ensure that the trusted_proxies setting in your app/config/config.yml is not set or + it will overwrite the setTrustedProxies call above. + That's it! It's critical that you prevent traffic from all non-trusted sources. If you allow outside traffic, they could "spoof" their true IP address and other information. From 4d8c89d2f3d3f1c79797c76d8022d007cbbd2245 Mon Sep 17 00:00:00 2001 From: Patrick McAndrew Date: Tue, 26 May 2015 09:04:55 +0100 Subject: [PATCH 0232/2667] Add formatting for file and method names --- cookbook/request/load_balancer_reverse_proxy.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/request/load_balancer_reverse_proxy.rst b/cookbook/request/load_balancer_reverse_proxy.rst index 650d9bf7421..6bd4d1661dc 100644 --- a/cookbook/request/load_balancer_reverse_proxy.rst +++ b/cookbook/request/load_balancer_reverse_proxy.rst @@ -83,8 +83,8 @@ In this case, you'll need to - *very carefully* - trust *all* proxies. $response = $kernel->handle($request); // ... -#. Ensure that the trusted_proxies setting in your app/config/config.yml is not set or - it will overwrite the setTrustedProxies call above. +#. Ensure that the trusted_proxies setting in your ``app/config/config.yml`` is not set or + it will overwrite the ``setTrustedProxies`` call above. That's it! It's critical that you prevent traffic from all non-trusted sources. If you allow outside traffic, they could "spoof" their true IP address and From da65156777 10000 9044bf71a59c7ce9307b64aa87da33 Mon Sep 17 00:00:00 2001 From: Patrick McAndrew Date: Tue, 2 Jun 2015 11:31:47 +0100 Subject: [PATCH 0233/2667] Adjust line wrapping --- cookbook/request/load_balancer_reverse_proxy.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/request/load_balancer_reverse_proxy.rst b/cookbook/request/load_balancer_reverse_proxy.rst index 6bd4d1661dc..60c8d23ff0d 100644 --- a/cookbook/request/load_balancer_reverse_proxy.rst +++ b/cookbook/request/load_balancer_reverse_proxy.rst @@ -83,8 +83,8 @@ In this case, you'll need to - *very carefully* - trust *all* proxies. $response = $kernel->handle($request); // ... -#. Ensure that the trusted_proxies setting in your ``app/config/config.yml`` is not set or - it will overwrite the ``setTrustedProxies`` call above. +#. Ensure that the trusted_proxies setting in your ``app/config/config.yml`` + is not set or it will overwrite the ``setTrustedProxies`` call above. That's it! It's critical that you prevent traffic from all non-trusted sources. If you allow outside traffic, they could "spoof" their true IP address and From 58bb3c559446c46cef154dbac7b08cf98c802fd7 Mon Sep 17 00:00:00 2001 From: Ana Cicconi Date: Sat, 23 May 2015 13:00:30 +0200 Subject: [PATCH 0234/2667] overriding 3rd party bundles --- best_practices/creating-the-project.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst index a180eef9fd2..bdf67108ce3 100644 --- a/best_practices/creating-the-project.rst +++ b/best_practices/creating-the-project.rst @@ -110,6 +110,13 @@ Symfony documentation uses the AppBundle name. There is no need to prefix the AppBundle with your own vendor (e.g. AcmeAppBundle), because this application bundle is never going to be shared. + +.. note:: + + Another reason to create a new bundle is when you're overriding something + in a vendor's bundle. For instance, if you have to override an action of + the FOSUserBundle, then you should create a UserBundle that would be used + only for this purpose. All in all, this is the typical directory structure of a Symfony application that follows these best practices: From c24862be171313603f590cdccb9d8e15b4099aac Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 27 Jun 2015 19:09:16 -0400 Subject: [PATCH 0235/2667] Minor tweak to remove FOSUserBundle example and replace with a link to the doc about this --- best_practices/creating-the-project.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst index bdf67108ce3..b3307100ea6 100644 --- a/best_practices/creating-the-project.rst +++ b/best_practices/creating-the-project.rst @@ -114,9 +114,7 @@ Symfony documentation uses the AppBundle name. .. note:: Another reason to create a new bundle is when you're overriding something - in a vendor's bundle. For instance, if you have to override an action of - the FOSUserBundle, then you should create a UserBundle that would be used - only for this purpose. + in a vendor's bundle (e.g. a controller). See :doc:`/cookbook/bundles/inheritance`. All in all, this is the typical directory structure of a Symfony application that follows these best practices: From 5467a56b3cd3298ca0b40757858c2a481df265e7 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 27 Jun 2015 19:15:10 -0400 Subject: [PATCH 0236/2667] [#5293] Tweak to link to the article about parent bundles --- cookbook/bundles/override.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cookbook/bundles/override.rst b/cookbook/bundles/override.rst index b67ae24c656..034acd5dc9f 100644 --- a/cookbook/bundles/override.rst +++ b/cookbook/bundles/override.rst @@ -191,10 +191,10 @@ can override the translations from any translation file, as long as it is in The last translation file always wins. That means that you need to make sure that the bundle containing *your* translations is loaded after any bundle whose translations you're overriding. This is done in ``AppKernel``. - Translation files are not aware of the inheritance tree and therefore - unaware of their parent. If you want to override translations from the - parent bundle, be sure that the parent bundle is loaded before the child - bundle in the ``AppKernel`` class. + + Translation files are also not aware of :doc:`bundle inheritance `. + If you want to override translations from the parent bundle, be sure that the + parent bundle is loaded before the child bundle in the ``AppKernel`` class. The file that always wins is the one that is placed in ``app/Resources/translations``, as those files are always loaded last. From fa3936b10aff93dc5972a6ea819f59973c52bf8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Neboj=C5=A1a=20Kamber?= Date: Sat, 27 Jun 2015 13:21:43 +0200 Subject: [PATCH 0237/2667] Fixing "Undefined method" error on Symfony 2.7 The code example tries to call `getToken` on `Symfony\Component\Security\Core\Authorization\AuthorizationChecker`, which fails on Symfony 2.7 because the method is undefined. This change utilizes the TokenStorage class, introduced in Symfony 2.6 --- cookbook/security/impersonating_user.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cookbook/security/impersonating_user.rst b/cookbook/security/impersonating_user.rst index f9003fd9b93..2e6775b6c22 100644 --- a/cookbook/security/impersonating_user.rst +++ b/cookbook/security/impersonating_user.rst @@ -93,9 +93,10 @@ over the user's roles until you find one that a ``SwitchUserRole`` object:: use Symfony\Component\Security\Core\Role\SwitchUserRole; $authChecker = $this->get('security.authorization_checker'); + $tokenStorage = $this->get('security.token_storage'); if ($authChecker->isGranted('ROLE_PREVIOUS_ADMIN')) { - foreach ($authChecker->getToken()->getRoles() as $role) { + foreach ($tokenStorage->getToken()->getRoles() as $role) { if ($role instanceof SwitchUserRole) { $impersonatingUser = $role->getSource()->getUser(); break; From 554e6d1a4502db8a2f95e576336bc6a1e7decee1 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 28 Jun 2015 09:48:48 +0200 Subject: [PATCH 0238/2667] [Cookbook][Assetic] complete a sentence --- cookbook/assetic/php.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cookbook/assetic/php.rst b/cookbook/assetic/php.rst index 6133497f498..6a704036ec0 100644 --- a/cookbook/assetic/php.rst +++ b/cookbook/assetic/php.rst @@ -116,7 +116,8 @@ formatter used by the filter to produce the compiled CSS file. Using the compressed formatter will minimize the the resulting file, regardless of whether the original files are regular CSS files or SCSS files. -Next, your Twig template to add the ``{% stylesheets %}`` tag defined by Assetic: +Next, update your Twig template to add the ``{% stylesheets %}`` tag defined +by Assetic: .. code-block:: html+jinja From 00e6d3efc2856b5110ce86a7cd5658127065662e Mon Sep 17 00:00:00 2001 From: Hari KT Date: Sat, 13 Jun 2015 14:45:02 +0530 Subject: [PATCH 0239/2667] Wrap the table creation inside the class extending Command, so users know where the comes. They can use it as standalone when needed --- components/console/helpers/table.rst | 31 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/components/console/helpers/table.rst b/components/console/helpers/table.rst index 0f3511fe040..acecdccfcbd 100644 --- a/components/console/helpers/table.rst +++ b/components/console/helpers/table.rst @@ -25,18 +25,25 @@ To display a table, use :class:`Symfony\\Component\\Console\\Helper\\Table`, set the headers, set the rows and then render the table:: use Symfony\Component\Console\Helper\Table; - - $table = new Table($output); - $table - ->setHeaders(array('ISBN', 'Title', 'Author')) - ->setRows(array( - array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), - array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), - array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), - array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), - )) - ; - $table->render(); + // ... + + class SomeCommand extends Command + { + public function execute(InputInterface $input, OutputInterface $output) + { + $table = new Table($output); + $table + ->setHeaders(array('ISBN', 'Title', 'Author')) + ->setRows(array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + )) + ; + $table->render(); + } + } You can add a table separator anywhere in the output by passing an instance of :class:`Symfony\\Component\\Console\\Helper\\TableSeparator` as a row:: From bacd22ee25ecec9a4bfb3e0877db484fd8511ffb Mon Sep 17 00:00:00 2001 From: Javier Spagnoletti Date: Mon, 15 Jun 2015 12:41:52 -0300 Subject: [PATCH 0240/2667] [Contributing] [Standards] Added entry for identical comparison --- contributing/code/standards.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index 40ca8c4981d..48864f6992b 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -100,6 +100,8 @@ Structure * Place unary operators (``!``, ``--``, ...) adjacent to the affected variable; +* Always use `identical comparison`_ unless you need type juggling; + * Add a comma after each array item in a multi-line array, even after the last one; @@ -186,3 +188,4 @@ License .. _`PSR-1`: http://www.php-fig.org/psr/psr-1/ .. _`PSR-2`: http://www.php-fig.org/psr/psr-2/ .. _`PSR-4`: http://www.php-fig.org/psr/psr-4/ +.. _`identical comparison`: https://php.net/manual/en/language.operators.comparison.php From 23f5bc8c37a285db80b69c3e242ab9c509c89d38 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 28 Jun 2015 12:09:40 +0200 Subject: [PATCH 0241/2667] tweak headline to be consistent with 2.6 --- cookbook/security/form_login_setup.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/security/form_login_setup.rst b/cookbook/security/form_login_setup.rst index bb4d4fa6afe..b254e27fdef 100644 --- a/cookbook/security/form_login_setup.rst +++ b/cookbook/security/form_login_setup.rst @@ -462,8 +462,8 @@ firewall matches *all* URLs, including ``/login_check``). If ``/login_check`` doesn't match any firewall, you'll receive a ``Unable to find the controller for path "/login_check"`` exception. -4. Multiple Firewalls Don't Share Security Context -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +4. Multiple Firewalls Don't Share the same Security Context +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you're using multiple firewalls and you authenticate against one firewall, you will *not* be authenticated against any other firewalls automatically. From 0ac631d8bf8be493ea5ec2fc1092cba8dc537d42 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 28 Jun 2015 13:04:25 +0200 Subject: [PATCH 0242/2667] fix capitalization --- cookbook/security/form_login_setup.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/security/form_login_setup.rst b/cookbook/security/form_login_setup.rst index b254e27fdef..5e20bef050d 100644 --- a/cookbook/security/form_login_setup.rst +++ b/cookbook/security/form_login_setup.rst @@ -462,7 +462,7 @@ firewall matches *all* URLs, including ``/login_check``). If ``/login_check`` doesn't match any firewall, you'll receive a ``Unable to find the controller for path "/login_check"`` exception. -4. Multiple Firewalls Don't Share the same Security Context +4. Multiple Firewalls Don't Share the Same Security Context ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you're using multiple firewalls and you authenticate against one firewall, From e495337e8dbbd1232b9a72ba3b86de14e607324c Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 28 Jun 2015 16:23:27 +0200 Subject: [PATCH 0243/2667] Rewrite note a bit --- cookbook/form/form_customization.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cookbook/form/form_customization.rst b/cookbook/form/form_customization.rst index 87aa62da84a..1d4c2a67b07 100644 --- a/cookbook/form/form_customization.rst +++ b/cookbook/form/form_customization.rst @@ -116,10 +116,9 @@ fragment needed to render every part of a form: .. caution:: - If you use the Bootstrap form theme and want to render the parts of - a checkbox field individually, do not use the Twig function {{ form_label() }}. - Otherwise, it will render nothing. - + When you use the Bootstrap form themes and render the fields manually, + calling ``form_label()`` for a checkbox/radio field doesn't show anything. + Due to Bootstrap internals, the label is already shown by ``form_widget()``. In the next section you will learn how to customize a theme by overriding some or all of its fragments. From 377c6a7d5d01a68f8f13ec8be764fb4db10ac7cf Mon Sep 17 00:00:00 2001 From: Sasha Matejic Date: Sun, 14 Jun 2015 21:59:12 +0200 Subject: [PATCH 0244/2667] Fix after install URL and new photo since AcmeDemoBundle is not part of 2.7 --- book/installation.rst | 3 ++- images/quick_tour/welcome.png | Bin 123539 -> 51365 bytes 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/book/installation.rst b/book/installation.rst index 37e89431904..c91f85830b7 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -169,7 +169,8 @@ browsing the project directory and executing this command: $ cd my_project_name/ $ php app/console server:run -Then, open your browser and access the ``http://localhost:8000`` URL to see the +Then, open your browser and access the ``http://localhost:8000/app/example`` +URL to see the Welcome page of Symfony: .. image:: /images/quick_tour/welcome.png diff --git a/images/quick_tour/welcome.png b/images/quick_tour/welcome.png index 75fce7f560fbdddf30662639f768b857205ffd88..1a9c527ff63f418f2da400dbdd73ce0f76831233 100644 GIT binary patch literal 51365 zcmeFabx>T*)-Q~c-~@L9K@wyjxI+jKLU4C?8Qg|JLU0f65Q2Mfhu{vub&%jXxDFrB zvG?S>_x^F~lY6T^sG_Rr-K%@;)k}W8dUq3|ASZ>1Mv4Xp2Zt&B;k^;%z&5m{CeAN4)hS9Np&IT^pzE3*M#+Cjz9^A^VjUO8-+&hNqz2Q;Z z;A;vK)0gD5mz=E2KB(D(qUXh>_ZJ$j3MR0J9gv z+x)75k2%wz&z@N^kWpyfDZEtkoiy{Al(+|3S!U<$<~k`gdYh171I7`=NrD0Vm`7llE2>Jyi^OTm9gi!Tw(wI}YK z$b1$SePd9RJhy)TlX;do1Vy!vlCMw^{+9-GWGV&G+p>txb7;I(JFkxSS&|vX2y1z8 z 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�n!V>FcVuz9^ZiTXYBuUIN1dL`mG-tOECb&J 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�t{;Ekcm%zrmtFe6lv5VlR~5_H39T8 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 bMR=!7lQVq_zuMCO0R1S+s>(D-nT7o?8i#E= From 2e89aed0e78caaee5f63a8c80963f60fb7c50e9b Mon Sep 17 00:00:00 2001 From: DQNEO Date: Sat, 20 Jun 2015 14:50:05 +0900 Subject: [PATCH 0245/2667] fix for Symfony 2.7 --- quick_tour/the_big_picture.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index 1399307e3ef..da37909b87a 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -89,7 +89,7 @@ the project directory and executing this command: $ cd myproject/ $ php app/console server:run -Open your browser and access the ``http://localhost:8000`` URL to see the +Open your browser and access the ``http://localhost:8000/app/example`` URL to see the welcome page of Symfony: .. image:: /images/quick_tour/welcome.png From d4ea1450501360c618c380017e7779ae69f28a76 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 28 Jun 2015 18:00:56 +0200 Subject: [PATCH 0246/2667] rework the quick tour's big picture --- quick_tour/the_big_picture.rst | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index da37909b87a..f458fdfc439 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -106,20 +106,16 @@ Congratulations! Your first Symfony project is up and running! them are explained in the :ref:`Setting up Permissions ` section of the official book. - + If the welcome page does not seem to be rendering CSS or image assets, install them first: - + .. code-block:: bash - + $ php app/console assets:install When you are finished working on your Symfony application, you can stop -the server with the ``server:stop`` command: - -.. code-block:: bash - - $ php app/console server:stop +the server by pressing Ctrl and C. .. tip:: @@ -135,15 +131,15 @@ of database calls, HTML tags and other PHP code in the same script. To achieve this goal with Symfony, you'll first need to learn a few fundamental concepts. When developing a Symfony application, your responsibility as a developer -is to write the code that maps the user's *request* (e.g. ``http://localhost:8000/``) -to the *resource* associated with it (the ``Welcome to Symfony!`` HTML page). +is to write the code that maps the user's *request* (e.g. ``http://localhost:8000/app/example``) +to the *resource* associated with it (the ``Homepage`` HTML page). The code to execute is defined in **actions** and **controllers**. The mapping between user's requests and that code is defined via the **routing** configuration. And the contents displayed in the browser are usually rendered using **templates**. -When you browsed ``http://localhost:8000/`` earlier, Symfony executed the -controller defined in the ``src/AppBundle/Controller/DefaultController.php`` +When you browsed ``http://localhost:8000/app/example`` earlier, Symfony executed +the controller defined in the ``src/AppBundle/Controller/DefaultController.php`` file and rendered the ``app/Resources/views/default/index.html.twig`` template. In the following sections you'll learn in detail the inner workings of Symfony controllers, routes and templates. @@ -186,7 +182,7 @@ information and then they render a template to show the results to the user. In this example, the ``index`` action is practically empty because it doesn't need to call any other method. The action just renders a template with the -*Welcome to Symfony!* content. +*Homepage.* content. Routing ~~~~~~~ @@ -221,8 +217,8 @@ start with ``/**``, whereas regular PHP comments start with ``/*``. The first value of ``@Route()`` defines the URL that will trigger the execution of the action. As you don't have to add the host of your application to the URL (e.g. ```http://example.com``), these URLs are always relative and -they are usually called *paths*. In this case, the ``/`` path refers to -the application homepage. The second value of ``@Route()`` (e.g. +they are usually called *paths*. In this case, the ``/app/example`` path +refers to the application homepage. The second value of ``@Route()`` (e.g. ``name="homepage"``) is optional and sets the name of this route. For now this name is not needed, but later it'll be useful for linking pages. From f28ef78de4b0e7028056ca2d7f9c453f652345d0 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 28 Jun 2015 12:48:36 -0400 Subject: [PATCH 0247/2667] changing a few orders of sections - mostly to move overridden after the main options --- reference/forms/types/integer.rst | 16 ++++++++-------- reference/forms/types/money.rst | 16 ++++++++-------- reference/forms/types/number.rst | 16 ++++++++-------- reference/forms/types/percent.rst | 16 ++++++++-------- reference/forms/types/text.rst | 16 ++++++++-------- 5 files changed, 40 insertions(+), 40 deletions(-) diff --git a/reference/forms/types/integer.rst b/reference/forms/types/integer.rst index 4a571df3652..d632729a488 100644 --- a/reference/forms/types/integer.rst +++ b/reference/forms/types/integer.rst @@ -16,13 +16,13 @@ integers. By default, all non-integer values (e.g. 6.78) will round down +-------------+-----------------------------------------------------------------------+ | Rendered as | ``input`` ``number`` field | +-------------+-----------------------------------------------------------------------+ -| Overridden | - `compound`_ | -| options | | -+-------------+-----------------------------------------------------------------------+ | Options | - `grouping`_ | | | - `precision`_ | | | - `rounding_mode`_ | +-------------+-----------------------------------------------------------------------+ +| Overridden | - `compound`_ | +| options | | ++-------------+-----------------------------------------------------------------------+ | Inherited | - `data`_ | | options | - `disabled`_ | | | - `empty_data`_ | @@ -41,11 +41,6 @@ integers. By default, all non-integer values (e.g. 6.78) will round down | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\IntegerType` | +-------------+-----------------------------------------------------------------------+ -Overridden Options ------------------- - -.. include:: /reference/forms/types/options/compound_type.rst.inc - Field Options ------------- @@ -74,6 +69,11 @@ on the :class:`Symfony\\Component\\Form\\Extension\\Core\\DataTransformer\\Integ * ``IntegerToLocalizedStringTransformer::ROUND_CEILING`` Rounding mode to round towards positive infinity. +Overridden Options +------------------ + +.. include:: /reference/forms/types/options/compound_type.rst.inc + Inherited Options ----------------- diff --git a/reference/forms/types/money.rst b/reference/forms/types/money.rst index a1a7247bf15..0f6fd6994aa 100644 --- a/reference/forms/types/money.rst +++ b/reference/forms/types/money.rst @@ -14,14 +14,14 @@ how the input and output of the data is handled. +-------------+---------------------------------------------------------------------+ | Rendered as | ``input`` ``text`` field | +-------------+---------------------------------------------------------------------+ -| Overridden | - `compound`_ | -| options | | -+-------------+---------------------------------------------------------------------+ | Options | - `currency`_ | | | - `divisor`_ | | | - `grouping`_ | | | - `precision`_ | +-------------+---------------------------------------------------------------------+ +| Overridden | - `compound`_ | +| options | | ++-------------+---------------------------------------------------------------------+ | Inherited | - `data`_ | | options | - `disabled`_ | | | - `empty_data`_ | @@ -40,11 +40,6 @@ how the input and output of the data is handled. | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\MoneyType` | +-------------+---------------------------------------------------------------------+ -Overridden Options ------------------- - -.. include:: /reference/forms/types/options/compound_type.rst.inc - Field Options ------------- @@ -91,6 +86,11 @@ you can modify this value. You probably won't need to do this unless, for example, you want to round to the nearest dollar (set the precision to ``0``). +Overridden Options +------------------ + +.. include:: /reference/forms/types/options/compound_type.rst.inc + Inherited Options ----------------- diff --git a/reference/forms/types/number.rst b/reference/forms/types/number.rst index 3dae2121195..509f10917f3 100644 --- a/reference/forms/types/number.rst +++ b/reference/forms/types/number.rst @@ -11,13 +11,13 @@ that you want to use for your number. +-------------+----------------------------------------------------------------------+ | Rendered as | ``input`` ``text`` field | +-------------+----------------------------------------------------------------------+ -| Overridden | - `compound`_ | -| options | | -+-------------+----------------------------------------------------------------------+ | Options | - `grouping`_ | | | - `precision`_ | | | - `rounding_mode`_ | +-------------+----------------------------------------------------------------------+ +| Overridden | - `compound`_ | +| options | | ++-------------+----------------------------------------------------------------------+ | Inherited | - `data`_ | | options | - `disabled`_ | | | - `empty_data`_ | @@ -36,11 +36,6 @@ that you want to use for your number. | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\NumberType` | +-------------+----------------------------------------------------------------------+ -Overridden Options ------------------- - -.. include:: /reference/forms/types/options/compound_type.rst.inc - Field Options ------------- @@ -82,6 +77,11 @@ option is a constant on the to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up. +Overridden Options +------------------ + +.. include:: /reference/forms/types/options/compound_type.rst.inc + Inherited Options ----------------- diff --git a/reference/forms/types/percent.rst b/reference/forms/types/percent.rst index 12aded78a3e..d048d085fd6 100644 --- a/reference/forms/types/percent.rst +++ b/reference/forms/types/percent.rst @@ -15,12 +15,12 @@ This field adds a percentage sign "``%``" after the input box. +-------------+-----------------------------------------------------------------------+ | Rendered as | ``input`` ``text`` field | +-------------+-----------------------------------------------------------------------+ -| Overridden | - `compound`_ | -| options | | -+-------------+-----------------------------------------------------------------------+ | Options | - `precision`_ | | | - `type`_ | +-------------+-----------------------------------------------------------------------+ +| Overridden | - `compound`_ | +| options | | ++-------------+-----------------------------------------------------------------------+ | Inherited | - `data`_ | | options | - `disabled`_ | | | - `empty_data`_ | @@ -39,11 +39,6 @@ This field adds a percentage sign "``%``" after the input box. | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\PercentType` | +-------------+-----------------------------------------------------------------------+ -Overridden Options ------------------- - -.. include:: /reference/forms/types/options/compound_type.rst.inc - Field Options ------------- @@ -75,6 +70,11 @@ object. The two "types" handle these two cases: The raw value (``55``) is shown to the user and stored on your object. Note that this only works for integer values. +Overridden Options +------------------ + +.. include:: /reference/forms/types/options/compound_type.rst.inc + Inherited Options ----------------- diff --git a/reference/forms/types/text.rst b/reference/forms/types/text.rst index dcd598de69e..d4933625fc8 100644 --- a/reference/forms/types/text.rst +++ b/reference/forms/types/text.rst @@ -9,9 +9,6 @@ The text field represents the most basic input text field. +-------------+--------------------------------------------------------------------+ | Rendered as | ``input`` ``text`` field | +-------------+--------------------------------------------------------------------+ -| Overridden | - `compound`_ | -| options | | -+-------------+--------------------------------------------------------------------+ | Inherited | - `data`_ | | options | - `disabled`_ | | | - `empty_data`_ | @@ -25,16 +22,14 @@ The text field represents the most basic input text field. | | - `required`_ | | | - `trim`_ | +-------------+--------------------------------------------------------------------+ +| Overridden | - `compound`_ | +| options | | ++-------------+--------------------------------------------------------------------+ | Parent type | :doc:`form ` | +-------------+--------------------------------------------------------------------+ | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\TextType` | +-------------+--------------------------------------------------------------------+ -Overridden Options ------------------- - -.. include:: /reference/forms/types/options/compound_type.rst.inc - Inherited Options ----------------- @@ -70,3 +65,8 @@ The default value is ``''`` (the empty string). .. include:: /reference/forms/types/options/required.rst.inc .. include:: /reference/forms/types/options/trim.rst.inc + +Overridden Options +------------------ + +.. include:: /reference/forms/types/options/compound_type.rst.inc From 082063547e851369018173e0cb0c474f590f1518 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 28 Jun 2015 12:53:58 -0400 Subject: [PATCH 0248/2667] moving options below basic usage --- reference/forms/types/file.rst | 40 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/reference/forms/types/file.rst b/reference/forms/types/file.rst index 188f0d0ec59..4b5faaa41d7 100644 --- a/reference/forms/types/file.rst +++ b/reference/forms/types/file.rst @@ -27,26 +27,6 @@ The ``file`` type represents a file input in your form. | Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\FileType` | +-------------+---------------------------------------------------------------------+ -Overridden Options ------------------- - -.. include:: /reference/forms/types/options/compound_type.rst.inc - -data_class -~~~~~~~~~~ - -**type**: ``string`` **default**: :class:`Symfony\\Component\\HttpFoundation\\File\\File` - -This option sets the appropriate file-related data mapper to be used by the type. - -empty_data -~~~~~~~~~~ - -**type**: ``mixed`` **default**: ``null`` - -This option determines what value the field will return when the submitted -value is empty. - Basic Usage ----------- @@ -97,6 +77,26 @@ before using it directly. Read the :doc:`cookbook ` for an example of how to manage a file upload associated with a Doctrine entity. +Overridden Options +------------------ + +.. include:: /reference/forms/types/options/compound_type.rst.inc + +data_class +~~~~~~~~~~ + +**type**: ``string`` **default**: :class:`Symfony\\Component\\HttpFoundation\\File\\File` + +This option sets the appropriate file-related data mapper to be used by the type. + +empty_data +~~~~~~~~~~ + +**type**: ``mixed`` **default**: ``null`` + +This option determines what value the field will return when the submitted +value is empty. + Inherited Options ----------------- From f528471152d11beb52373b53a5fa0e914f230588 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 28 Jun 2015 13:03:49 -0400 Subject: [PATCH 0249/2667] [#5335] Minor tweaks --- components/serializer.rst | 2 ++ cookbook/serializer.rst | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/components/serializer.rst b/components/serializer.rst index 29b3a7a3134..9328733ddf4 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -202,6 +202,8 @@ like the following:: // For YAML // $classMetadataFactory = new ClassMetadataFactory(new YamlFileLoader('/path/to/your/definition.yml')); +.. _component-serializer-attributes-groups-annotations: + Then, create your groups definition: .. configuration-block:: diff --git a/cookbook/serializer.rst b/cookbook/serializer.rst index 28c63e05b87..2e12c6cf641 100644 --- a/cookbook/serializer.rst +++ b/cookbook/serializer.rst @@ -76,7 +76,7 @@ Adding Normalizers and Encoders .. versionadded:: 2.7 The :class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` - is enabled by default in Symfony 2.7. In prior versions, you need to load + is enabled by default in Symfony 2.7. In prior versions, you needed to load your own normalizer. Once enabled, the ``serializer`` service will be available in the container @@ -161,6 +161,15 @@ with the following configuration: ), )); +Next, add the :ref:`@Groups annotations ` +to your class and choose which groups to use when serializing:: + + $serializer = $this->get('serializer'); + $json = $serializer->serialize( + $someObject, + 'json', array('groups' => array('group1') + ); + Enabling the Metadata Cache --------------------------- From 9f62c19a994f1d40fb3bc2a16db39a4d16f25232 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 28 Jun 2015 13:10:48 -0400 Subject: [PATCH 0250/2667] [#5332] typo --- components/serializer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/serializer.rst b/components/serializer.rst index ed205ac68ab..235ce8acf58 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -504,7 +504,7 @@ There are several types of normalizers available: :class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` This normalizer leverages the :doc:`PropertyAccess Component ` to read and write in the object. It means that it can access to properties - directly and trough getters, setters, hassers, adders and removers. It supports + directly and through getters, setters, hassers, adders and removers. It supports calling the constructor during the denormalization process. Objects are normalized to a map of property names (method name stripped of From 2f81cddbe19a216bd995c60f8283d73608b631ac Mon Sep 17 00:00:00 2001 From: Richard van Laak Date: Fri, 5 Jun 2015 14:07:50 +0200 Subject: [PATCH 0251/2667] Document security.switch_user event ... in the cookbook article about How to Impersonate a User. Added code sample about how to change the locale in case of a sticky locale: http://symfony.com/doc/current/cookbook/session/locale_sticky_session.html --- cookbook/security/impersonating_user.rst | 60 ++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/cookbook/security/impersonating_user.rst b/cookbook/security/impersonating_user.rst index 48a1adf498b..dc254f42900 100644 --- a/cookbook/security/impersonating_user.rst +++ b/cookbook/security/impersonating_user.rst @@ -151,3 +151,63 @@ setting: ), ), )); + +Events +------ + +The firewall dispatches the ``security.switch_user`` event right after the impersonation +is completed. The :class:`Symfony\\Component\\Security\\Http\\Event\\SwitchUserEvent` is +passed to the listener, and you can use this to get the user that you are now impersonating. + +The cookbook article about +:doc:`Making the Locale "Sticky" during a User's Session ` +does not update the locale when you impersonate a user. The following code sample will show +how to change the sticky locale: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/services.yml + services: + app.switch_user_listener: + class: AppBundle\EventListener\SwitchUserListener + tags: + - { name: kernel.event_listener, event: security.switch_user, method: onSwitchUser } + + .. code-block:: xml + + + + + + + .. code-block:: php + + // app/config/services.php + $container + ->register('app.switch_user_listener', 'AppBundle\EventListener\SwitchUserListener') + ->addTag('kernel.event_listener', array('event' => 'security.switch_user', 'method' => 'onSwitchUser')) + ; + +.. caution:: + + The listener implementation assumes your ``User`` entity has a ``getLocale()`` method. + +.. code-block:: php + + // src/AppBundle/EventListener/SwitchUserListener.pnp + namespace AppBundle\EventListener; + + use Symfony\Component\Security\Http\Event\SwitchUserEvent; + + class SwitchUserListener + { + public function onSwitchUser(SwitchUserEvent $event) + { + $event->getRequest()->getSession()->set( + '_locale', + $event->getTargetUser()->getLocale() + ); + } + } From 1fa69fe10df17351160a59c80c73854babad781a Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Mon, 18 Aug 2014 13:40:46 +0200 Subject: [PATCH 0252/2667] Added information about the new date handling in the comparison constraints and Range Conflicts: reference/constraints/GreaterThan.rst reference/constraints/GreaterThanOrEqual.rst reference/constraints/LessThan.rst reference/constraints/LessThanOrEqual.rst --- reference/constraints/GreaterThan.rst | 217 ++++++++++++++++--- reference/constraints/GreaterThanOrEqual.rst | 217 ++++++++++++++++--- reference/constraints/LessThan.rst | 202 +++++++++++++++-- reference/constraints/LessThanOrEqual.rst | 210 +++++++++++++++--- reference/constraints/Range.rst | 198 +++++++++++++++++ 5 files changed, 934 insertions(+), 110 deletions(-) diff --git a/reference/constraints/GreaterThan.rst b/reference/constraints/GreaterThan.rst index b1066c6b1f4..a8638acf025 100644 --- a/reference/constraints/GreaterThan.rst +++ b/reference/constraints/GreaterThan.rst @@ -4,8 +4,8 @@ GreaterThan .. versionadded:: 2.3 The ``GreaterThan`` constraint was introduced in Symfony 2.3. -Validates that a value is greater than another value, defined in the options. -To force that a value is greater than or equal to another value, see +Validates that a value is greater than another value, defined in the options. To +force that a value is greater than or equal to another value, see :doc:`/reference/constraints/GreaterThanOrEqual`. To force a value is less than another value, see :doc:`/reference/constraints/LessThan`. @@ -14,7 +14,6 @@ than another value, see :doc:`/reference/constraints/LessThan`. +----------------+---------------------------------------------------------------------------+ | Options | - `value`_ | | 741A | - `message`_ | -| | - `payload`_ | +----------------+---------------------------------------------------------------------------+ | Class | :class:`Symfony\\Component\\Validator\\Constraints\\GreaterThan` | +----------------+---------------------------------------------------------------------------+ @@ -24,11 +23,20 @@ than another value, see :doc:`/reference/constraints/LessThan`. Basic Usage ----------- -If you want to ensure that the ``age`` of a ``Person`` class is greater -than ``18``, you could do the following: +If you want to ensure that the ``age`` of a ``Person`` class is greater than +``18``, you could do the following: .. configuration-block:: + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - GreaterThan: + value: 18 + .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Person.php @@ -46,31 +54,16 @@ than ``18``, you could do the following: protected $age; } - .. code-block:: yaml - - # src/Acme/SocialBundle/Resources/config/validation.yml - Acme\SocialBundle\Entity\Person: - properties: - age: - - GreaterThan: - value: 18 - .. code-block:: xml - - - - - - - - - - - + + + + + + + .. code-block:: php @@ -90,6 +83,170 @@ than ``18``, you could do the following: } } +Comparing Dates +--------------- + +This constraint can be used to compare ``DateTime`` objects against any date +string `accepted by the DateTime constructor`_. For example, you could check +that a date must at least be the next day: + +.. configuration-block:: + + .. code-block:: yaml + + # src/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThan: today + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + /** + * @Assert\GreaterThan("today") + */ + protected $deliveryDate; + } + + .. code-block:: xml + + + + + today + + + + .. code-block:: php + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('deliveryDate', new Assert\GreaterThan('today')); + } + } + +Be aware that PHP will use the server's configured timezone to interpret these +dates. If you want to fix the timezone, append it to the date string: + +.. configuration-block:: + + .. code-block:: yaml + + # src/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThan: today UTC + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + /** + * @Assert\GreaterThan("today UTC") + */ + protected $deliveryDate; + } + + .. code-block:: xml + + + + + today UTC + + + + .. code-block:: php + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('deliveryDate', new Assert\GreaterThan('today UTC')); + } + } + +The ``DateTime`` class also accepts relative dates or times. For example, you +can check that the above delivery date starts at least five hours after the +current time: + +.. configuration-block:: + + .. code-block:: yaml + + # src/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThan: +5 hours + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + /** + * @Assert\GreaterThan("+5 hours") + */ + protected $deliveryDate; + } + + .. code-block:: xml + + + + + +5 hours + + + + .. code-block:: php + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('deliveryDate', new Assert\GreaterThan('+5 hours')); + } + } + Options ------- @@ -100,7 +257,7 @@ message **type**: ``string`` **default**: ``This value should be greater than {{ compared_value }}.`` -This is the message that will be shown if the value is not greater than -the comparison value. +This is the message that will be shown if the value is not greater than the +comparison value. -.. include:: /reference/constraints/_payload-option.rst.inc +.. _`accepted by the DateTime constructor`: http://www.php.net/manual/en/datetime.formats.php diff --git a/reference/constraints/GreaterThanOrEqual.rst b/reference/constraints/GreaterThanOrEqual.rst index cc868390490..bdc6f9e36cf 100644 --- a/reference/constraints/GreaterThanOrEqual.rst +++ b/reference/constraints/GreaterThanOrEqual.rst @@ -4,8 +4,8 @@ GreaterThanOrEqual .. versionadded:: 2.3 The ``GreaterThanOrEqual`` constraint was introduced in Symfony 2.3. -Validates that a value is greater than or equal to another value, defined -in the options. To force that a value is greater than another value, see +Validates that a value is greater than or equal to another value, defined in +the options. To force that a value is greater than another value, see :doc:`/reference/constraints/GreaterThan`. +----------------+----------------------------------------------------------------------------------+ @@ -13,7 +13,6 @@ in the options. To force that a value is greater than another value, see +----------------+----------------------------------------------------------------------------------+ | Options | - `value`_ | | | - `message`_ | -| | - `payload`_ | +----------------+----------------------------------------------------------------------------------+ | Class | :class:`Symfony\\Component\\Validator\\Constraints\\GreaterThanOrEqual` | +----------------+----------------------------------------------------------------------------------+ @@ -23,11 +22,20 @@ in the options. To force that a value is greater than another value, see Basic Usage ----------- -If you want to ensure that the ``age`` of a ``Person`` class is greater -than or equal to ``18``, you could do the following: +If you want to ensure that the ``age`` of a ``Person`` class is greater than +or equal to ``18``, you could do the following: .. configuration-block:: + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - GreaterThanOrEqual: + value: 18 + .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Person.php @@ -45,31 +53,16 @@ than or equal to ``18``, you could do the following: protected $age; } - .. code-block:: yaml - - # src/Acme/SocialBundle/Resources/config/validation.yml - Acme\SocialBundle\Entity\Person: - properties: - age: - - GreaterThanOrEqual: - value: 18 - .. code-block:: xml - - - - - - - - - - - + + + + + + + .. code-block:: php @@ -89,6 +82,170 @@ than or equal to ``18``, you could do the following: } } +Comparing Dates +--------------- + +This constraint can be used to compare ``DateTime`` objects against any date +string `accepted by the DateTime constructor`_. For example, you could check +that a date must at least be the current day: + +.. configuration-block:: + + .. code-block:: yaml + + # src/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThanOrEqual: today + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + /** + * @Assert\GreaterThanOrEqual("today") + */ + protected $deliveryDate; + } + + .. code-block:: xml + + + + + today + + + + .. code-block:: php + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('deliveryDate', new Assert\GreaterThanOrEqual('today')); + } + } + +Be aware that PHP will use the server's configured timezone to interpret these +dates. If you want to fix the timezone, append it to the date string: + +.. configuration-block:: + + .. code-block:: yaml + + # src/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThanOrEqual: today UTC + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + /** + * @Assert\GreaterThanOrEqual("today UTC") + */ + protected $deliveryDate; + } + + .. code-block:: xml + + + + + today UTC + + + + .. code-block:: php + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('deliveryDate', new Assert\GreaterThanOrEqual('today UTC')); + } + } + +The ``DateTime`` class also accepts relative dates or times. For example, you +can check that the above delivery date starts at least five hours after the +current time: + +.. configuration-block:: + + .. code-block:: yaml + + # src/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThanOrEqual: +5 hours + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + /** + * @Assert\GreaterThanOrEqual("+5 hours") + */ + protected $deliveryDate; + } + + .. code-block:: xml + + + + + +5 hours + + + + .. code-block:: php + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('deliveryDate', new Assert\GreaterThanOrEqual('+5 hours')); + } + } + Options ------- @@ -99,7 +256,7 @@ message **type**: ``string`` **default**: ``This value should be greater than or equal to {{ compared_value }}.`` -This is the message that will be shown if the value is not greater than -or equal to the comparison value. +This is the message that will be shown if the value is not greater than or equal +to the comparison value. -.. include:: /reference/constraints/_payload-option.rst.inc +.. _`accepted by the DateTime constructor`: http://www.php.net/manual/en/datetime.formats.php diff --git a/reference/constraints/LessThan.rst b/reference/constraints/LessThan.rst index d14e289fd0c..09c5f93ef40 100644 --- a/reference/constraints/LessThan.rst +++ b/reference/constraints/LessThan.rst @@ -4,8 +4,8 @@ LessThan .. versionadded:: 2.3 The ``LessThan`` constraint was introduced in Symfony 2.3. -Validates that a value is less than another value, defined in the options. -To force that a value is less than or equal to another value, see +Validates that a value is less than another value, defined in the options. To +force that a value is less than or equal to another value, see :doc:`/reference/constraints/LessThanOrEqual`. To force a value is greater than another value, see :doc:`/reference/constraints/GreaterThan`. @@ -14,7 +14,6 @@ than another value, see :doc:`/reference/constraints/GreaterThan`. +----------------+------------------------------------------------------------------------+ | Options | - `value`_ | | | - `message`_ | -| | - `payload`_ | +----------------+------------------------------------------------------------------------+ | Class | :class:`Symfony\\Component\\Validator\\Constraints\\LessThan` | +----------------+------------------------------------------------------------------------+ @@ -29,6 +28,15 @@ If you want to ensure that the ``age`` of a ``Person`` class is less than .. configuration-block:: + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThan: + value: 80 + .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Person.php @@ -46,31 +54,75 @@ If you want to ensure that the ``age`` of a ``Person`` class is less than protected $age; } + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\LessThan(array( + 'value' => 80, + ))); + } + } + +Comparing Dates +--------------- + +This constraint can be used to compare ``DateTime`` objects against any date +string `accepted by the DateTime constructor`_. For example, you could check +that a date must be in the past like this: + +.. configuration-block:: + .. code-block:: yaml - # src/Acme/SocialBundle/Resources/config/validation.yml + # src/SocialBundle/Resources/config/validation.yml Acme\SocialBundle\Entity\Person: properties: age: - - LessThan: - value: 80 + - LessThan: today + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\LessThan("today") + */ + protected $age; + } .. code-block:: xml - - - - - - - - - - - + + + today + + .. code-block:: php @@ -84,9 +136,113 @@ If you want to ensure that the ``age`` of a ``Person`` class is less than { public static function loadValidatorMetadata(ClassMetadata $metadata) { - $metadata->addPropertyConstraint('age', new Assert\LessThan(array( - 'value' => 80, - ))); + $metadata->addPropertyConstraint('age', new Assert\LessThan('today')); + } + } + +Be aware that PHP will use the server's configured timezone to interpret these +dates. If you want to fix the timezone, append it to the date string: + +.. configuration-block:: + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThan: today UTC + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\LessThan("today UTC") + */ + protected $age; + } + + .. code-block:: xml + + + + + today UTC + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\LessThan('today UTC')); + } + } + +The ``DateTime`` class also accepts relative dates or times. For example, you +can check that a person must be at least 18 years old like this: + +.. configuration-block:: + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThan: -18 years + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\LessThan("-18 years") + */ + protected $age; + } + + .. code-block:: xml + + + + + -18 years + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\LessThan('-18 years')); } } @@ -103,4 +259,4 @@ message This is the message that will be shown if the value is not less than the comparison value. -.. include:: /reference/constraints/_payload-option.rst.inc +.. _`accepted by the DateTime constructor`: http://www.php.net/manual/en/datetime.formats.php diff --git a/reference/constraints/LessThanOrEqual.rst b/reference/constraints/LessThanOrEqual.rst index c75266bbc52..61261fbe571 100644 --- a/reference/constraints/LessThanOrEqual.rst +++ b/reference/constraints/LessThanOrEqual.rst @@ -4,8 +4,8 @@ LessThanOrEqual .. versionadded:: 2.3 The ``LessThanOrEqual`` constraint was introduced in Symfony 2.3. -Validates that a value is less than or equal to another value, defined in -the options. To force that a value is less than another value, see +Validates that a value is less than or equal to another value, defined in the +options. To force that a value is less than another value, see :doc:`/reference/constraints/LessThan`. +----------------+-------------------------------------------------------------------------------+ @@ -13,7 +13,6 @@ the options. To force that a value is less than another value, see +----------------+-------------------------------------------------------------------------------+ | Options | - `value`_ | | | - `message`_ | -| | - `payload`_ | +----------------+-------------------------------------------------------------------------------+ | Class | :class:`Symfony\\Component\\Validator\\Constraints\\LessThanOrEqual` | +----------------+-------------------------------------------------------------------------------+ @@ -23,11 +22,20 @@ the options. To force that a value is less than another value, see Basic Usage ----------- -If you want to ensure that the ``age`` of a ``Person`` class is less than -or equal to ``80``, you could do the following: +If you want to ensure that the ``age`` of a ``Person`` class is less than or +equal to ``80``, you could do the following: .. configuration-block:: + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThanOrEqual: + value: 80 + .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Person.php @@ -45,31 +53,75 @@ or equal to ``80``, you could do the following: protected $age; } + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint( F438 'age', new Assert\LessThanOrEqual(array( + 'value' => 80, + ))); + } + } + +Comparing Dates +--------------- + +This constraint can be used to compare ``DateTime`` objects against any date +string `accepted by the DateTime constructor`_. For example, you could check +that a date must be today or in the past like this: + +.. configuration-block:: + .. code-block:: yaml - # src/Acme/SocialBundle/Resources/config/validation.yml + # src/SocialBundle/Resources/config/validation.yml Acme\SocialBundle\Entity\Person: properties: age: - - LessThanOrEqual: - value: 80 + - LessThanOrEqual: today + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\LessThanOrEqual("today") + */ + protected $age; + } .. code-block:: xml - - - - - - - - - - - + + + today + + .. code-block:: php @@ -83,9 +135,113 @@ or equal to ``80``, you could do the following: { public static function loadValidatorMetadata(ClassMetadata $metadata) { - $metadata->addPropertyConstraint('age', new Assert\LessThanOrEqual(array( - 'value' => 80, - ))); + $metadata->addPropertyConstraint('age', new Assert\LessThanOrEqual('today')); + } + } + +Be aware that PHP will use the server's configured timezone to interpret these +dates. If you want to fix the timezone, append it to the date string: + +.. configuration-block:: + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThanOrEqual: today UTC + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\LessThanOrEqual("today UTC") + */ + protected $age; + } + + .. code-block:: xml + + + + + today UTC + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\LessThanOrEqual('today UTC')); + } + } + +The ``DateTime`` class also accepts relative dates or times. For example, you +can check that a person must be at least 18 years old like this: + +.. configuration-block:: + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThanOrEqual: -18 years + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\LessThanOrEqual("-18 years") + */ + protected $age; + } + + .. code-block:: xml + + + + + -18 years + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\LessThanOrEqual('-18 years')); } } @@ -99,7 +255,7 @@ message **type**: ``string`` **default**: ``This value should be less than or equal to {{ compared_value }}.`` -This is the message that will be shown if the value is not less than or -equal to the comparison value. +This is the message that will be shown if the value is not less than or equal +to the comparison value. -.. include:: /reference/constraints/_payload-option.rst.inc +.. _`accepted by the DateTime constructor`: http://www.php.net/manual/en/datetime.formats.php diff --git a/reference/constraints/Range.rst b/reference/constraints/Range.rst index b588cc31cca..6009adf7a55 100644 --- a/reference/constraints/Range.rst +++ b/reference/constraints/Range.rst @@ -99,6 +99,203 @@ you might add the following: } } +Date Ranges +----------- + +This constraint can be used to compare ``DateTime`` objects against date ranges. +The minimum and maximum date of the range should be given as any date string +`accepted by the DateTime constructor`_. For example, you could check that a +date must lie within the current year like this: + +.. configuration-block:: + + .. code-block:: yaml + + # src/EventBundle/Resources/config/validation.yml + Acme\EventBundle\Entity\Event: + properties: + startDate: + - Range: + min: first day of January + max: first day of January next year + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Event.php + namespace Acme\EventBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Event + { + /** + * @Assert\Range( + * min = "first day of January", + * max = "first day of January next year" + * ) + */ + protected $startDate; + } + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + // src/Acme/EventBundle/Entity/Person.php + namespace Acme\EventBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Event + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('startDate', new Assert\Range(array( + 'min' => 'first day of January', + 'max' => 'first day of January next year', + ))); + } + } + +Be aware that PHP will use the server's configured timezone to interpret these +dates. If you want to fix the timezone, append it to the date string: + +.. configuration-block:: + + .. code-block:: yaml + + # src/EventBundle/Resources/config/validation.yml + Acme\EventBundle\Entity\Event: + properties: + startDate: + - Range: + min: first day of January UTC + max: first day of January next year UTC + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Event.php + namespace Acme\EventBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Event + { + /** + * @Assert\Range( + * min = "first day of January UTC", + * max = "first day of January next year UTC" + * ) + */ + protected $startDate; + } + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + // src/Acme/EventBundle/Entity/Person.php + namespace Acme\EventBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Event + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('startDate', new Assert\Range(array( + 'min' => 'first day of January UTC', + 'max' => 'first day of January next year UTC', + ))); + } + } + +The ``DateTime`` class also accepts relative dates or times. For example, you +can check that a delivery date starts within the next five hours like this: + +.. configuration-block:: + + .. code-block:: yaml + + # src/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - Range: + min: now + max: +5 hours + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + /** + * @Assert\Range( + * min = "now", + * max = "+5 hours" + * ) + */ + protected $deliveryDate; + } + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('deliveryDate', new Assert\Range(array( + 'min' => 'now', + 'max' => '+5 hours', + ))); + } + } + Options ------- @@ -145,3 +342,4 @@ the `is_numeric`_ PHP function). .. include:: /reference/constraints/_payload-option.rst.inc .. _`is_numeric`: http://www.php.net/manual/en/function.is-numeric.php +.. _`accepted by the DateTime constructor`: http://www.php.net/manual/en/datetime.formats.php From 70034452d978b25deb1bf4b0422dff1216d33968 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 11 Jun 2015 14:56:52 +0200 Subject: [PATCH 0253/2667] Finished the documentation of the new data comparison validators --- reference/constraints/GreaterThan.rst | 83 ++++++++++++------- reference/constraints/GreaterThanOrEqual.rst | 71 +++++++++++----- reference/constraints/LessThan.rst | 87 +++++++++++++------- reference/constraints/LessThanOrEqual.rst | 71 +++++++++++----- reference/constraints/Range.rst | 68 +++++++++------ 5 files changed, 253 insertions(+), 127 deletions(-) diff --git a/reference/constraints/GreaterThan.rst b/reference/constraints/GreaterThan.rst index a8638acf025..8f7783b1937 100644 --- a/reference/constraints/GreaterThan.rst +++ b/reference/constraints/GreaterThan.rst @@ -57,13 +57,19 @@ If you want to ensure that the ``age`` of a ``Person`` class is greater than .. code-block:: xml - - - - - - - + + + + + + + + + + + .. code-block:: php @@ -86,6 +92,9 @@ If you want to ensure that the ``age`` of a ``Person`` class is greater than Comparing Dates --------------- +.. versionadded:: 2.6 + The feature to compare dates was added in Symfony 2.6. + This constraint can be used to compare ``DateTime`` objects against any date string `accepted by the DateTime constructor`_. For example, you could check that a date must at least be the next day: @@ -94,7 +103,7 @@ that a date must at least be the next day: .. code-block:: yaml - # src/OrderBundle/Resources/config/validation.yml + # src/Acme/OrderBundle/Resources/config/validation.yml Acme\OrderBundle\Entity\Order: properties: deliveryDate: @@ -102,7 +111,7 @@ that a date must at least be the next day: .. code-block:: php-annotations - // src/Acme/SocialBundle/Entity/Order.php + // src/Acme/OrderBundle/Entity/Order.php namespace Acme\OrderBundle\Entity; use Symfony\Component\Validator\Constraints as Assert; @@ -118,11 +127,17 @@ that a date must at least be the next day: .. code-block:: xml - - - today - - + + + + + + today + + + .. code-block:: php @@ -147,7 +162,7 @@ dates. If you want to fix the timezone, append it to the date string: .. code-block:: yaml - # src/OrderBundle/Resources/config/validation.yml + # src/Acme/OrderBundle/Resources/config/validation.yml Acme\OrderBundle\Entity\Order: properties: deliveryDate: @@ -155,7 +170,7 @@ dates. If you want to fix the timezone, append it to the date string: .. code-block:: php-annotations - // src/Acme/SocialBundle/Entity/Order.php + // src/Acme/OrderBundle/Entity/Order.php namespace Acme\OrderBundle\Entity; use Symfony\Component\Validator\Constraints as Assert; @@ -171,11 +186,17 @@ dates. If you want to fix the timezone, append it to the date string: .. code-block:: xml - - - today UTC - - + + + + + + today UTC + + + .. code-block:: php @@ -201,7 +222,7 @@ current time: .. code-block:: yaml - # src/OrderBundle/Resources/config/validation.yml + # src/Acme/OrderBundle/Resources/config/validation.yml Acme\OrderBundle\Entity\Order: properties: deliveryDate: @@ -209,7 +230,7 @@ current time: .. code-block:: php-annotations - // src/Acme/SocialBundle/Entity/Order.php + // src/Acme/OrderBundle/Entity/Order.php namespace Acme\OrderBundle\Entity; use Symfony\Component\Validator\Constraints as Assert; @@ -225,11 +246,17 @@ current time: .. code-block:: xml - - - +5 hours - - + + + + + + +5 hours + + + .. code-block:: php diff --git a/reference/constraints/GreaterThanOrEqual.rst b/reference/constraints/GreaterThanOrEqual.rst index bdc6f9e36cf..e5abbfb239f 100644 --- a/reference/constraints/GreaterThanOrEqual.rst +++ b/reference/constraints/GreaterThanOrEqual.rst @@ -56,13 +56,19 @@ or equal to ``18``, you could do the following: .. code-block:: xml - - - - - - - + + + + + + + + + + + .. code-block:: php @@ -85,6 +91,9 @@ or equal to ``18``, you could do the following: Comparing Dates --------------- +.. versionadded:: 2.6 + The feature to compare dates was added in Symfony 2.6. + This constraint can be used to compare ``DateTime`` objects against any date string `accepted by the DateTime constructor`_. For example, you could check that a date must at least be the current day: @@ -117,11 +126,17 @@ that a date must at least be the current day: .. code-block:: xml - - - today - - + + + + + + today + + + .. code-block:: php @@ -170,11 +185,17 @@ dates. If you want to fix the timezone, append it to the date string: .. code-block:: xml - - - today UTC - - + + + + + + today UTC + + + .. code-block:: php @@ -224,11 +245,17 @@ current time: .. code-block:: xml - - - +5 hours - - + + + + + + +5 hours + + + .. code-block:: php diff --git a/reference/constraints/LessThan.rst b/reference/constraints/LessThan.rst index 09c5f93ef40..f4a2f32af40 100644 --- a/reference/constraints/LessThan.rst +++ b/reference/constraints/LessThan.rst @@ -57,13 +57,19 @@ If you want to ensure that the ``age`` of a ``Person`` class is less than .. code-block:: xml - - - - - - - + + + + + + + + + + + .. code-block:: php @@ -86,6 +92,9 @@ If you want to ensure that the ``age`` of a ``Person`` class is less than Comparing Dates --------------- +.. versionadded:: 2.6 + The feature to compare dates was added in Symfony 2.6. + This constraint can be used to compare ``DateTime`` objects against any date string `accepted by the DateTime constructor`_. For example, you could check that a date must be in the past like this: @@ -97,7 +106,7 @@ that a date must be in the past like this: # src/SocialBundle/Resources/config/validation.yml Acme\SocialBundle\Entity\Person: properties: - age: + dateOfBirth: - LessThan: today .. code-block:: php-annotations @@ -112,17 +121,23 @@ that a date must be in the past like this: /** * @Assert\LessThan("today") */ - protected $age; + protected $dateOfBirth; } .. code-block:: xml - - - today - - + + + + + + today + + + .. code-block:: php @@ -136,7 +151,7 @@ that a date must be in the past like this: { public static function loadValidatorMetadata(ClassMetadata $metadata) { - $metadata->addPropertyConstraint('age', new Assert\LessThan('today')); + $metadata->addPropertyConstraint('dateOfBirth', new Assert\LessThan('today')); } } @@ -150,7 +165,7 @@ dates. If you want to fix the timezone, append it to the date string: # src/SocialBundle/Resources/config/validation.yml Acme\SocialBundle\Entity\Person: properties: - age: + dateOfBirth: - LessThan: today UTC .. code-block:: php-annotations @@ -165,17 +180,23 @@ dates. If you want to fix the timezone, append it to the date string: /** * @Assert\LessThan("today UTC") */ - protected $age; + protected $dateOfBirth; } .. code-block:: xml - - - today UTC - - + + + + + + today UTC + + + .. code-block:: php @@ -203,7 +224,7 @@ can check that a person must be at least 18 years old like this: # src/SocialBundle/Resources/config/validation.yml Acme\SocialBundle\Entity\Person: properties: - age: + dateOfBirth: - LessThan: -18 years .. code-block:: php-annotations @@ -218,17 +239,23 @@ can check that a person must be at least 18 years old like this: /** * @Assert\LessThan("-18 years") */ - protected $age; + protected $dateOfBirth; } .. code-block:: xml - - - -18 years - - + + + + + + -18 years + + + .. code-block:: php @@ -242,7 +269,7 @@ can check that a person must be at least 18 years old like this: { public static function loadValidatorMetadata(ClassMetadata $metadata) { - $metadata->addPropertyConstraint('age', new Assert\LessThan('-18 years')); + $metadata->addPropertyConstraint('dateOfBirth', new Assert\LessThan('-18 years')); } } diff --git a/reference/constraints/LessThanOrEqual.rst b/reference/constraints/LessThanOrEqual.rst index 61261fbe571..89d67ff41b4 100644 --- a/reference/constraints/LessThanOrEqual.rst +++ b/reference/constraints/LessThanOrEqual.rst @@ -56,13 +56,19 @@ equal to ``80``, you could do the following: .. code-block:: xml - - - - - - - + + + + + + + + + + + .. code-block:: php @@ -85,6 +91,9 @@ equal to ``80``, you could do the following: Comparing Dates --------------- +.. versionadded:: 2.6 + The feature to compare dates was added in Symfony 2.6. + This constraint can be used to compare ``DateTime`` objects against any date string `accepted by the DateTime constructor`_. For example, you could check that a date must be today or in the past like this: @@ -117,11 +126,17 @@ that a date must be today or in the past like this: .. code-block:: xml - - - today - - + + + + + + today + + + .. code-block:: php @@ -170,11 +185,17 @@ dates. If you want to fix the timezone, append it to the date string: .. code-block:: xml - - - today UTC - - + + + + + + today UTC + + + .. code-block:: php @@ -223,11 +244,17 @@ can check that a person must be at least 18 years old like this: .. code-block:: xml - - - -18 years - - + + + + + + -18 years + + + .. code-block:: php diff --git a/reference/constraints/Range.rst b/reference/constraints/Range.rst index 6009adf7a55..1dce92d6a59 100644 --- a/reference/constraints/Range.rst +++ b/reference/constraints/Range.rst @@ -140,18 +140,24 @@ date must lie within the current year like this: .. code-block:: xml - - - - - - - - + + + + + + + + + + + + .. code-block:: php - // src/Acme/EventBundle/Entity/Person.php + // src/Acme/EventBundle/Entity/Event.php namespace Acme\EventBundle\Entity; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -204,14 +210,20 @@ dates. If you want to fix the timezone, append it to the date string: .. code-block:: xml - - - - - - - - + + + + + + + + + + + + .. code-block:: php @@ -268,14 +280,20 @@ can check that a delivery date starts within the next five hours like this: .. code-block:: xml - - - - - - - - + + + + + + + + + + + + .. code-block:: php From 39f46e1902abc51f0c3ffc448e68015a5aa07d08 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 16 Jun 2015 16:22:01 +0200 Subject: [PATCH 0254/2667] Fixed the issues reported by @xabbuh --- reference/constraints/GreaterThan.rst | 7 +++++-- reference/constraints/GreaterThanOrEqual.rst | 4 ++-- reference/constraints/LessThan.rst | 4 ++-- reference/constraints/LessThanOrEqual.rst | 4 ++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/reference/constraints/GreaterThan.rst b/reference/constraints/GreaterThan.rst index 8f7783b1937..ccc89c9e92f 100644 --- a/reference/constraints/GreaterThan.rst +++ b/reference/constraints/GreaterThan.rst @@ -14,6 +14,7 @@ than another value, see :doc:`/reference/constraints/LessThan`. +----------------+---------------------------------------------------------------------------+ | Options | - `value`_ | | | - `message`_ | +| | - `payload`_ | +----------------+---------------------------------------------------------------------------+ | Class | :class:`Symfony\\Component\\Validator\\Constraints\\GreaterThan` | +----------------+---------------------------------------------------------------------------+ @@ -30,7 +31,7 @@ If you want to ensure that the ``age`` of a ``Person`` class is greater than .. code-block:: yaml - # src/SocialBundle/Resources/config/validation.yml + # src/Acme/SocialBundle/Resources/config/validation.yml Acme\SocialBundle\Entity\Person: properties: age: @@ -93,7 +94,7 @@ Comparing Dates --------------- .. versionadded:: 2.6 - The feature to compare dates was added in Symfony 2.6. + The feature to compare dates was introduced in Symfony 2.6. This constraint can be used to compare ``DateTime`` objects against any date string `accepted by the DateTime constructor`_. For example, you could check @@ -287,4 +288,6 @@ message This is the message that will be shown if the value is not greater than the comparison value. +.. include:: /reference/constraints/_payload-option.rst.inc + .. _`accepted by the DateTime constructor`: http://www.php.net/manual/en/datetime.formats.php diff --git a/reference/constraints/GreaterThanOrEqual.rst b/reference/constraints/GreaterThanOrEqual.rst index e5abbfb239f..b591f38d0b4 100644 --- a/reference/constraints/GreaterThanOrEqual.rst +++ b/reference/constraints/GreaterThanOrEqual.rst @@ -29,7 +29,7 @@ or equal to ``18``, you could do the following: .. code-block:: yaml - # src/SocialBundle/Resources/config/validation.yml + # src/Acme/SocialBundle/Resources/config/validation.yml Acme\SocialBundle\Entity\Person: properties: age: @@ -92,7 +92,7 @@ Comparing Dates --------------- .. versionadded:: 2.6 - The feature to compare dates was added in Symfony 2.6. + The feature to compare dates was introduced in Symfony 2.6. This constraint can be used to compare ``DateTime`` objects against any date string `accepted by the DateTime constructor`_. For example, you could check diff --git a/reference/constraints/LessThan.rst b/reference/constraints/LessThan.rst index f4a2f32af40..d07c163abfb 100644 --- a/reference/constraints/LessThan.rst +++ b/reference/constraints/LessThan.rst @@ -30,7 +30,7 @@ If you want to ensure that the ``age`` of a ``Person`` class is less than .. code-block:: yaml - # src/SocialBundle/Resources/config/validation.yml + # src/Acme/SocialBundle/Resources/config/validation.yml Acme\SocialBundle\Entity\Person: properties: age: @@ -93,7 +93,7 @@ Comparing Dates --------------- .. versionadded:: 2.6 - The feature to compare dates was added in Symfony 2.6. + The feature to compare dates was introduced in Symfony 2.6. This constraint can be used to compare ``DateTime`` objects against any date string `accepted by the DateTime constructor`_. For example, you could check diff --git a/reference/constraints/LessThanOrEqual.rst b/reference/constraints/LessThanOrEqual.rst index 89d67ff41b4..78c40bb9c95 100644 --- a/reference/constraints/LessThanOrEqual.rst +++ b/reference/constraints/LessThanOrEqual.rst @@ -29,7 +29,7 @@ equal to ``80``, you could do the following: .. code-block:: yaml - # src/SocialBundle/Resources/config/validation.yml + # src/Acme/SocialBundle/Resources/config/validation.yml Acme\SocialBundle\Entity\Person: properties: age: @@ -92,7 +92,7 @@ Comparing Dates --------------- .. versionadded:: 2.6 - The feature to compare dates was added in Symfony 2.6. + The feature to compare dates was introduced in Symfony 2.6. This constraint can be used to compare ``DateTime`` objects against any date string `accepted by the DateTime constructor`_. For example, you could check From e3efbbf81f2785ad36bf2f72e3ee174bf5c57d9b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 16 Jun 2015 16:26:57 +0200 Subject: [PATCH 0255/2667] Reordered the code blocks to show Annotations, YAML, XML and PHP --- reference/constraints/GreaterThan.rst | 66 ++++++++++---------- reference/constraints/GreaterThanOrEqual.rst | 66 ++++++++++---------- reference/constraints/LessThan.rst | 66 ++++++++++---------- reference/constraints/LessThanOrEqual.rst | 66 ++++++++++---------- 4 files changed, 132 insertions(+), 132 deletions(-) diff --git a/reference/constraints/GreaterThan.rst b/reference/constraints/GreaterThan.rst index ccc89c9e92f..bf47027494d 100644 --- a/reference/constraints/GreaterThan.rst +++ b/reference/constraints/GreaterThan.rst @@ -29,15 +29,6 @@ If you want to ensure that the ``age`` of a ``Person`` class is greater than .. configuration-block:: - .. code-block:: yaml - - # src/Acme/SocialBundle/Resources/config/validation.yml - Acme\SocialBundle\Entity\Person: - properties: - age: - - GreaterThan: - value: 18 - .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Person.php @@ -55,6 +46,15 @@ If you want to ensure that the ``age`` of a ``Person`` class is greater than protected $age; } + .. code-block:: yaml + + # src/Acme/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - GreaterThan: + value: 18 + .. code-block:: xml @@ -102,14 +102,6 @@ that a date must at least be the next day: .. configuration-block:: - .. code-block:: yaml - - # src/Acme/OrderBundle/Resources/config/validation.yml - Acme\OrderBundle\Entity\Order: - properties: - deliveryDate: - - GreaterThan: today - .. code-block:: php-annotations // src/Acme/OrderBundle/Entity/Order.php @@ -125,6 +117,14 @@ that a date must at least be the next day: protected $deliveryDate; } + .. code-block:: yaml + + # src/Acme/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThan: today + .. code-block:: xml @@ -161,14 +161,6 @@ dates. If you want to fix the timezone, append it to the date string: .. configuration-block:: - .. code-block:: yaml - - # src/Acme/OrderBundle/Resources/config/validation.yml - Acme\OrderBundle\Entity\Order: - properties: - deliveryDate: - - GreaterThan: today UTC - .. code-block:: php-annotations // src/Acme/OrderBundle/Entity/Order.php @@ -184,6 +176,14 @@ dates. If you want to fix the timezone, append it to the date string: protected $deliveryDate; } + .. code-block:: yaml + + # src/Acme/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThan: today UTC + .. code-block:: xml @@ -221,14 +221,6 @@ current time: .. configuration-block:: - .. code-block:: yaml - - # src/Acme/OrderBundle/Resources/config/validation.yml - Acme\OrderBundle\Entity\Order: - properties: - deliveryDate: - - GreaterThan: +5 hours - .. code-block:: php-annotations // src/Acme/OrderBundle/Entity/Order.php @@ -244,6 +236,14 @@ current time: protected $deliveryDate; } + .. code-block:: yaml + + # src/Acme/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThan: +5 hours + .. code-block:: xml diff --git a/reference/constraints/GreaterThanOrEqual.rst b/reference/constraints/GreaterThanOrEqual.rst index b591f38d0b4..eb5754c2eaa 100644 --- a/reference/constraints/GreaterThanOrEqual.rst +++ b/reference/constraints/GreaterThanOrEqual.rst @@ -27,15 +27,6 @@ or equal to ``18``, you could do the following: .. configuration-block:: - .. code-block:: yaml - - # src/Acme/SocialBundle/Resources/config/validation.yml - Acme\SocialBundle\Entity\Person: - properties: - age: - - GreaterThanOrEqual: - value: 18 - .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Person.php @@ -53,6 +44,15 @@ or equal to ``18``, you could do the following: protected $age; } + .. code-block:: yaml + + # src/Acme/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - GreaterThanOrEqual: + value: 18 + .. code-block:: xml @@ -100,14 +100,6 @@ that a date must at least be the current day: .. configuration-block:: - .. code-block:: yaml - - # src/OrderBundle/Resources/config/validation.yml - Acme\OrderBundle\Entity\Order: - properties: - deliveryDate: - - GreaterThanOrEqual: today - .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Order.php @@ -123,6 +115,14 @@ that a date must at least be the current day: protected $deliveryDate; } + .. code-block:: yaml + + # src/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThanOrEqual: today + .. code-block:: xml @@ -159,14 +159,6 @@ dates. If you want to fix the timezone, append it to the date string: .. configuration-block:: - .. code-block:: yaml - - # src/OrderBundle/Resources/config/validation.yml - Acme\OrderBundle\Entity\Order: - properties: - deliveryDate: - - GreaterThanOrEqual: today UTC - .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Order.php @@ -182,6 +174,14 @@ dates. If you want to fix the timezone, append it to the date string: protected $deliveryDate; } + .. code-block:: yaml + + # src/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThanOrEqual: today UTC + .. code-block:: xml @@ -219,14 +219,6 @@ current time: .. configuration-block:: - .. code-block:: yaml - - # src/OrderBundle/Resources/config/validation.yml - Acme\OrderBundle\Entity\Order: - properties: - deliveryDate: - - GreaterThanOrEqual: +5 hours - .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Order.php @@ -242,6 +234,14 @@ current time: protected $deliveryDate; } + .. code-block:: yaml + + # src/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThanOrEqual: +5 hours + .. code-block:: xml diff --git a/reference/constraints/LessThan.rst b/reference/constraints/LessThan.rst index d07c163abfb..3afcd1be4f1 100644 --- a/reference/constraints/LessThan.rst +++ b/reference/constraints/LessThan.rst @@ -28,15 +28,6 @@ If you want to ensure that the ``age`` of a ``Person`` class is less than .. configuration-block:: - .. code-block:: yaml - - # src/Acme/SocialBundle/Resources/config/validation.yml - Acme\SocialBundle\Entity\Person: - properties: - age: - - LessThan: - value: 80 - .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Person.php @@ -54,6 +45,15 @@ If you want to ensure that the ``age`` of a ``Person`` class is less than protected $age; } + .. code-block:: yaml + + # src/Acme/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThan: + value: 80 + .. code-block:: xml @@ -101,14 +101,6 @@ that a date must be in the past like this: .. configuration-block:: - .. code-block:: yaml - - # src/SocialBundle/Resources/config/validation.yml - Acme\SocialBundle\Entity\Person: - properties: - dateOfBirth: - - LessThan: today - .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Person.php @@ -124,6 +116,14 @@ that a date must be in the past like this: protected $dateOfBirth; } + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + dateOfBirth: + - LessThan: today + .. code-block:: xml @@ -160,14 +160,6 @@ dates. If you want to fix the timezone, append it to the date string: .. configuration-block:: - .. code-block:: yaml - - # src/SocialBundle/Resources/config/validation.yml - Acme\SocialBundle\Entity\Person: - properties: - dateOfBirth: - - LessThan: today UTC - .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Person.php @@ -183,6 +175,14 @@ dates. If you want to fix the timezone, append it to the date string: protected $dateOfBirth; } + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + dateOfBirth: + - LessThan: today UTC + .. code-block:: xml @@ -219,14 +219,6 @@ can check that a person must be at least 18 years old like this: .. configuration-block:: - .. code-block:: yaml - - # src/SocialBundle/Resources/config/validation.yml - Acme\SocialBundle\Entity\Person: - properties: - dateOfBirth: - - LessThan: -18 years - .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Person.php @@ -242,6 +234,14 @@ can check that a person must be at least 18 years old like this: protected $dateOfBirth; } + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + dateOfBirth: + - LessThan: -18 years + .. code-block:: xml diff --git a/reference/constraints/LessThanOrEqual.rst b/reference/constraints/LessThanOrEqual.rst index 78c40bb9c95..889ffe9d46a 100644 --- a/reference/constraints/LessThanOrEqual.rst +++ b/reference/constraints/LessThanOrEqual.rst @@ -27,15 +27,6 @@ equal to ``80``, you could do the following: .. configuration-block:: - .. code-block:: yaml - - # src/Acme/SocialBundle/Resources/config/validation.yml - Acme\SocialBundle\Entity\Person: - properties: - age: - - LessThanOrEqual: - value: 80 - .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Person.php @@ -53,6 +44,15 @@ equal to ``80``, you could do the following: protected $age; } + .. code-block:: yaml + + # src/Acme/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThanOrEqual: + value: 80 + .. code-block:: xml @@ -100,14 +100,6 @@ that a date must be today or in the past like this: .. configuration-block:: - .. code-block:: yaml - - # src/SocialBundle/Resources/config/validation.yml - Acme\SocialBundle\Entity\Person: - properties: - age: - - LessThanOrEqual: today - .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Person.php @@ -123,6 +115,14 @@ that a date must be today or in the past like this: protected $age; } + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThanOrEqual: today + .. code-block:: xml @@ -159,14 +159,6 @@ dates. If you want to fix the timezone, append it to the date string: .. configuration-block:: - .. code-block:: yaml - - # src/SocialBundle/Resources/config/validation.yml - Acme\SocialBundle\Entity\Person: - properties: - age: - - LessThanOrEqual: today UTC - .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Person.php @@ -182,6 +174,14 @@ dates. If you want to fix the timezone, append it to the date string: protected $age; } + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThanOrEqual: today UTC + .. code-block:: xml @@ -218,14 +218,6 @@ can check that a person must be at least 18 years old like this: .. configuration-block:: - .. code-block:: yaml - - # src/SocialBundle/Resources/config/validation.yml - Acme\SocialBundle\Entity\Person: - properties: - age: - - LessThanOrEqual: -18 years - .. code-block:: php-annotations // src/Acme/Socia 10000 lBundle/Entity/Person.php @@ -241,6 +233,14 @@ can check that a person must be at least 18 years old like this: protected $age; } + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThanOrEqual: -18 years + .. code-block:: xml From 7ef2e6a09294bb4662f6842ee15735cd45551675 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 16 Jun 2015 22:26:30 +0200 Subject: [PATCH 0256/2667] Show annotations first --- reference/constraints/Range.rst | 60 ++++++++++++++++----------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/reference/constraints/Range.rst b/reference/constraints/Range.rst index 1dce92d6a59..6f4dea3339d 100644 --- a/reference/constraints/Range.rst +++ b/reference/constraints/Range.rst @@ -109,16 +109,6 @@ date must lie within the current year like this: .. configuration-block:: - .. code-block:: yaml - - # src/EventBundle/Resources/config/validation.yml - Acme\EventBundle\Entity\Event: - properties: - startDate: - - Range: - min: first day of January - max: first day of January next year - .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Event.php @@ -137,6 +127,16 @@ date must lie within the current year like this: protected $startDate; } + .. code-block:: yaml + + # src/EventBundle/Resources/config/validation.yml + Acme\EventBundle\Entity\Event: + properties: + startDate: + - Range: + min: first day of January + max: first day of January next year + .. code-block:: xml @@ -179,16 +179,6 @@ dates. If you want to fix the timezone, append it to the date string: .. configuration-block:: - .. code-block:: yaml - - # src/EventBundle/Resources/config/validation.yml - Acme\EventBundle\Entity\Event: - properties: - startDate: - - Range: - min: first day of January UTC - max: first day of January next year UTC - .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Event.php @@ -207,6 +197,16 @@ dates. If you want to fix the timezone, append it to the date string: protected $startDate; } + .. code-block:: yaml + + # src/EventBundle/Resources/config/validation.yml + Acme\EventBundle\Entity\Event: + properties: + startDate: + - Range: + min: first day of January UTC + max: first day of January next year UTC + .. code-block:: xml @@ -249,16 +249,6 @@ can check that a delivery date starts within the next five hours like this: .. configuration-block:: - .. code-block:: yaml - - # src/OrderBundle/Resources/config/validation.yml - Acme\OrderBundle\Entity\Order: - properties: - deliveryDate: - - Range: - min: now - max: +5 hours - .. code-block:: php-annotations // src/Acme/SocialBundle/Entity/Order.php @@ -277,6 +267,16 @@ can check that a delivery date starts within the next five hours like this: protected $deliveryDate; } + .. code-block:: yaml + + # src/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - Range: + min: now + max: +5 hours + .. code-block:: xml From b6c1a93e7e9a11ff99b9ae46c81ebce3ef9d60e6 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 16 Jun 2015 22:27:45 +0200 Subject: [PATCH 0257/2667] Added the "payload" option back --- reference/constraints/GreaterThanOrEqual.rst | 3 +++ reference/constraints/LessThan.rst | 3 +++ reference/constraints/LessThanOrEqual.rst | 3 +++ 3 files changed, 9 insertions(+) diff --git a/reference/constraints/GreaterThanOrEqual.rst b/reference/constraints/GreaterThanOrEqual.rst index eb5754c2eaa..4d61dd5b077 100644 --- a/reference/constraints/GreaterThanOrEqual.rst +++ b/reference/constraints/GreaterThanOrEqual.rst @@ -13,6 +13,7 @@ the options. To force that a value is greater than another value, see +----------------+----------------------------------------------------------------------------------+ | Options | - `value`_ | | | - `message`_ | +| | - `payload`_ | +----------------+----------------------------------------------------------------------------------+ | Class | :class:`Symfony\\Component\\Validator\\Constraints\\GreaterThanOrEqual` | +----------------+----------------------------------------------------------------------------------+ @@ -286,4 +287,6 @@ message This is the message that will be shown if the value is not greater than or equal to the comparison value. +.. include:: /reference/constraints/_payload-option.rst.inc + .. _`accepted by the DateTime constructor`: http://www.php.net/manual/en/datetime.formats.php diff --git a/reference/constraints/LessThan.rst b/reference/constraints/LessThan.rst index 3afcd1be4f1..772f6e629ee 100644 --- a/reference/constraints/LessThan.rst +++ b/reference/constraints/LessThan.rst @@ -14,6 +14,7 @@ than another value, see :doc:`/reference/constraints/GreaterThan`. +----------------+------------------------------------------------------------------------+ | Options | - `value`_ | | | - `message`_ | +| | - `payload`_ | +----------------+------------------------------------------------------------------------+ | Class | :class:`Symfony\\Component\\Validator\\Constraints\\LessThan` | +----------------+------------------------------------------------------------------------+ @@ -286,4 +287,6 @@ message This is the message that will be shown if the value is not less than the comparison value. +.. include:: /reference/constraints/_payload-option.rst.inc + .. _`accepted by the DateTime constructor`: http://www.php.net/manual/en/datetime.formats.php diff --git a/reference/constraints/LessThanOrEqual.rst b/reference/constraints/LessThanOrEqual.rst index 889ffe9d46a..22d0201a429 100644 --- a/reference/constraints/LessThanOrEqual.rst +++ b/reference/constraints/LessThanOrEqual.rst @@ -13,6 +13,7 @@ options. To force that a value is less than another value, see +----------------+-------------------------------------------------------------------------------+ | Options | - `value`_ | | | - `message`_ | +| | - `payload`_ | +----------------+-------------------------------------------------------------------------------+ | Class | :class:`Symfony\\Component\\Validator\\Constraints\\LessThanOrEqual` | +----------------+-------------------------------------------------------------------------------+ @@ -285,4 +286,6 @@ message This is the message that will be shown if the value is not less than or equal to the comparison value. +.. include:: /reference/constraints/_payload-option.rst.inc + .. _`accepted by the DateTime constructor`: http://www.php.net/manual/en/datetime.formats.php From 023a96fe15b3fe31cec933d4e6da8cf37251e312 Mon Sep 17 00:00:00 2001 From: Javier Spagnoletti Date: Mon, 15 Jun 2015 12:16:30 -0300 Subject: [PATCH 0258/2667] [Contributing] [Conventions] Added entry for Yoda conditions | Q | A | ------------- | --- | Doc fix? | yes | New docs? | no | Applies to | 2.8+ | Fixed tickets | --- contributing/code/standards.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index 48864f6992b..025a3376dde 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -102,6 +102,9 @@ Structure * Always use `identical comparison`_ unless you need type juggling; +* Use `Yoda conditions`_ when checking a variable against an expression to avoid + an accidental assignment inside the condition statement; + * Add a comma after each array item in a multi-line array, even after the last one; @@ -189,3 +192,4 @@ License .. _`PSR-2`: http://www.php-fig.org/psr/psr-2/ .. _`PSR-4`: http://www.php-fig.org/psr/psr-4/ .. _`identical comparison`: https://php.net/manual/en/language.operators.comparison.php +.. _`Yoda conditions`: https://en.wikipedia.org/wiki/Yoda_conditions From d606f3e0ccf43dddb00a7b0323ad7dc7acdb9888 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 28 Jun 2015 13:55:09 -0400 Subject: [PATCH 0259/2667] [#5402] Being explicit what this applies to (it should not apply to things like >=) --- contributing/code/standards.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index 025a3376dde..2168129d7fa 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -103,7 +103,8 @@ Structure * Always use `identical comparison`_ unless you need type juggling; * Use `Yoda conditions`_ when checking a variable against an expression to avoid - an accidental assignment inside the condition statement; + an accidental assignment inside the condition statement (this applies to ``==``, + ``!=``, ``===``, and ``!==``); * Add a comma after each array item in a multi-line array, even after the last one; From c9a40019d6b159f00a3a00ac2843302cf43d1ace Mon Sep 17 00:00:00 2001 From: Thomas Schulz Date: Tue, 16 Jun 2015 09:51:09 +0200 Subject: [PATCH 0260/2667] Changed PhpStormOpener to PhpStormProtocol PhpStormOpener only supports Mac OS. PhpStormProtocol supports Windows, so the sentence here now makes sense. --- reference/configuration/framework.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 9de35e6bc0b..d2fdb146b5a 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -256,7 +256,7 @@ you use PHPstorm on the Mac OS platform, you will do something like: .. tip:: - If you're on a Windows PC, you can install the `PhpStormOpener`_ to + If you're on a Windows PC, you can install the `PhpStormProtocol`_ to be able to use this. Of course, since every developer uses a different IDE, it's better to set @@ -1529,4 +1529,4 @@ Full Default Configuration .. _`HTTP Host header attacks`: http://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html .. _`Security Advisory Blog post`: http://symfony.com/blog/security-releases-symfony-2-0-24-2-1-12-2-2-5-and-2-3-3-released#cve-2013-4752-request-gethost-poisoning .. _`Doctrine Cache`: http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/caching.html -.. _`PhpStormOpener`: https://github.com/pinepain/PhpStormOpener +.. _`PhpStormProtocol`: https://github.com/aik099/PhpStormProtocol From b3414deca9e78917c4279b918bfe0d3ce28edf0a Mon Sep 17 00:00:00 2001 From: Oliver Forral Date: Mon, 29 Jun 2015 11:00:28 -0700 Subject: [PATCH 0261/2667] data transformers cookbook service definition typo --- cookbook/form/data_transformers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index c2ce3ae0486..850db1a5adb 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -149,7 +149,7 @@ to explicitly choose the one to use. To achieve this, you can use a factory:: app.type.task: class: AppBundle\Form\TaskType arguments: ["@app.issue_transformer_factory"] - tag: + tags: - { name: form.type, alias: app_task } .. code-block:: xml From e30b430c148fb72071afb84ef9558306e99c9c31 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 29 Jun 2015 21:33:00 +0200 Subject: [PATCH 0262/2667] use HTTPS for links to symfony.com --- CONTRIBUTING.md | 4 +- README.markdown | 6 +-- best_practices/business-logic.rst | 2 +- best_practices/controllers.rst | 2 +- best_practices/introduction.rst | 2 +- book/doctrine.rst | 12 ++--- book/http_cache.rst | 2 +- book/security.rst | 2 +- components/http_kernel/introduction.rst | 6 +-- components/security/authentication.rst | 2 +- contributing/code/bugs.rst | 2 +- contributing/code/security.rst | 62 ++++++++++++------------ contributing/community/releases.rst | 8 +-- contributing/documentation/overview.rst | 2 +- contributing/documentation/standards.rst | 2 +- cookbook/controller/error_pages.rst | 7 ++- cookbook/controller/service.rst | 2 +- cookbook/doctrine/registration_form.rst | 2 +- cookbook/security/entity_provider.rst | 6 +-- cookbook/templating/PHP.rst | 2 +- cookbook/workflow/new_project_git.rst | 2 +- cookbook/workflow/new_project_svn.rst | 2 +- reference/configuration/framework.rst | 2 +- 23 files changed, 70 insertions(+), 71 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 278f7c2c39c..da449d7c265 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,6 @@ Contributing ------------ We love contributors! For more information on how you can contribute to the -Symfony documentation, please read [Contributing to the Documentation](http://symfony.com/doc/current/contributing/documentation/overview.html) -and notice the [Pull Request Format](http://symfony.com/doc/current/contributing/documentation/overview.html#pull-request-format) +Symfony documentation, please read [Contributing to the Documentation](https://symfony.com/doc/current/contributing/documentation/overview.html) +and notice the [Pull Request Format](https://symfony.com/doc/current/contributing/documentation/overview.html#pull-request-format) that helps us merge your pull requests faster! diff --git a/README.markdown b/README.markdown index 9e17f5d4b7f..98cc5d386f5 100644 --- a/README.markdown +++ b/README.markdown @@ -1,16 +1,16 @@ Symfony Documentation ===================== -This documentation is rendered online at http://symfony.com/doc/current/ +This documentation is rendered online at https://symfony.com/doc/current/ Contributing ------------ >**Note** ->Unless you're documenting a feature that was introduced *after* Symfony 2.3 +>Unless you're documenting a feature that was introduced *after* Symfony 2.3 >(e.g. in Symfony 2.4), all pull requests must be based off of the **2.3** branch, >**not** the master or older branches. We love contributors! For more information on how you can contribute to the Symfony documentation, please read -[Contributing to the Documentation](http://symfony.com/doc/current/contributing/documentation/overview.html) +[Contributing to the Documentation](https://symfony.com/doc/current/contributing/documentation/overview.html) diff --git a/best_practices/business-logic.rst b/best_practices/business-logic.rst index ecfd9f276c8..394125f6d05 100644 --- a/best_practices/business-logic.rst +++ b/best_practices/business-logic.rst @@ -335,7 +335,7 @@ coding standards of an entire codebase in a matter of seconds. .. _`full definition`: http://en.wikipedia.org/wiki/Business_logic .. _`Doctrine project`: http://www.doctrine-project.org/ -.. _`fixture class`: http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html#writing-simple-fixtures +.. _`fixture class`: https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html#writing-simple-fixtures .. _`PSR-1`: http://www.php-fig.org/psr/psr-1/ .. _`PSR-2`: http://www.php-fig.org/psr/psr-2/ .. _`PHP-CS-Fixer`: https://github.com/FriendsOfPHP/PHP-CS-Fixer diff --git a/best_practices/controllers.rst b/best_practices/controllers.rst index 406d1b63c5d..8764de69c43 100644 --- a/best_practices/controllers.rst +++ b/best_practices/controllers.rst @@ -210,4 +210,4 @@ If you need to execute some code before or after the execution of your controlle you can use the EventDispatcher component to :doc:`set up before and after filters `. -.. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html +.. _`ParamConverter`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html diff --git a/best_practices/introduction.rst b/best_practices/introduction.rst index cd7c0a655f4..2c5661c6671 100644 --- a/best_practices/introduction.rst +++ b/best_practices/introduction.rst @@ -103,4 +103,4 @@ practices**. The reasons for not doing it are various: your tests or adding features that provide real value to the end users. .. _`Fabien Potencier`: https://connect.sensiolabs.com/profile/fabpot -.. _`download and install`: http://symfony.com/download +.. _`download and install`: https://symfony.com/download diff --git a/book/doctrine.rst b/book/doctrine.rst index 63c15079822..304edfc43bf 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -435,7 +435,7 @@ mapping information) of a bundle or an entire namespace: # generates all entities in the AppBundle $ php app/console doctrine:generate:entities AppBundle - + # generates all entities of bundles in the Acme namespace $ php app/console doctrine:generate:entities Acme @@ -651,7 +651,7 @@ to easily fetch objects based on multiple conditions:: If you click the icon, the profiler will open, showing you the exact queries that were made. - + The icon will turn yellow if there were more than 50 queries on the page. This could indicate that something is not correct. @@ -1428,8 +1428,8 @@ For more information about Doctrine, see the *Doctrine* section of the .. _`Lifecycle Events documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#lifecycle-events .. _`Reserved SQL keywords documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#quoting-reserved-words .. _`Persistent classes`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#persistent-classes -.. _`DoctrineMongoDBBundle`: http://symfony.com/doc/current/bundles/DoctrineMongoDBBundle/index.html -.. _`migrations`: http://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html -.. _`DoctrineFixturesBundle`: http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html -.. _`FrameworkExtraBundle documentation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html +.. _`DoctrineMongoDBBundle`: https://symfony.com/doc/current/bundles/DoctrineMongoDBBundle/index.html +.. _`migrations`: https://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html +.. _`DoctrineFixturesBundle`: https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html +.. _`FrameworkExtraBundle documentation`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html .. _`newer utf8mb4 character set`: https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html diff --git a/book/http_cache.rst b/book/http_cache.rst index 8d7332d8d8b..489c88e482a 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -1254,6 +1254,6 @@ Learn more from the Cookbook .. _`HTTP Bis`: http://tools.ietf.org/wg/httpbis/ .. _`P4 - Conditional Requests`: http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional .. _`P6 - Caching: Browser and intermediary caches`: http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache -.. _`FrameworkExtraBundle documentation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/cache.html +.. _`FrameworkExtraBundle documentation`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/cache.html .. _`ESI`: http://www.w3.org/TR/esi-lang .. _`FOSHttpCacheBundle`: http://foshttpcachebundle.readthedocs.org/ diff --git a/book/security.rst b/book/security.rst index 9bfe3c2809a..e99e70b6a6b 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1287,5 +1287,5 @@ Learn More from the Cookbook * :doc:`/cookbook/security/multiple_user_providers` .. _`online tool`: https://www.dailycred.com/blog/12/bcrypt-calculator -.. _`frameworkextrabundle documentation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html +.. _`frameworkextrabundle documentation`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html .. _`HWIOAuthBundle`: https://github.com/hwi/HWIOAuthBundle diff --git a/components/http_kernel/introduction.rst b/components/http_kernel/introduction.rst index a75ff31aeb6..ae3cf59fcef 100644 --- a/components/http_kernel/introduction.rst +++ b/components/http_kernel/introduction.rst @@ -708,7 +708,7 @@ look like this:: .. _FOSRestBundle: https://github.com/friendsofsymfony/FOSRestBundle .. _`Create your own framework... on top of the Symfony2 Components`: http://fabien.potencier.org/article/50/create-your-own-framework-on-top-of-the-symfony2-components-part-1 .. _`PHP FPM`: http://php.net/manual/en/install.fpm.php -.. _`SensioFrameworkExtraBundle`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html -.. _`@ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html -.. _`@Template`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/view.html +.. _`SensioFrameworkExtraBundle`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html +.. _`@ParamConverter`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html +.. _`@Template`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/view.html .. _`EmailSenderListener`: https://github.com/symfony/SwiftmailerBundle/blob/master/EventListener/EmailSenderListener.php diff --git a/components/security/authentication.rst b/components/security/authentication.rst index f07ed016c13..ddee75e221e 100644 --- a/components/security/authentication.rst +++ b/components/security/authentication.rst @@ -268,5 +268,5 @@ in) is correct, you can use:: $user->getSalt() ); -.. _`CVE-2013-5750`: http://symfony.com/blog/cve-2013-5750-security-issue-in-fosuserbundle-login-form +.. _`CVE-2013-5750`: https://symfony.com/blog/cve-2013-5750-security-issue-in-fosuserbundle-login-form .. _`BasePasswordEncoder::checkPasswordLength`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Core/Encoder/BasePasswordEncoder.php diff --git a/contributing/code/bugs.rst b/contributing/code/bugs.rst index d4c26b9bee6..ff7a54bc013 100644 --- a/contributing/code/bugs.rst +++ b/contributing/code/bugs.rst @@ -35,6 +35,6 @@ If your problem definitely looks like a bug, report it using the official bug * *(optional)* Attach a :doc:`patch `. .. _`Stack Overflow`: http://stackoverflow.com/questions/tagged/symfony2 -.. _IRC channel: http://symfony.com/irc +.. _IRC channel: https://symfony.com/irc .. _tracker: https://github.com/symfony/symfony/issues .. _Symfony Standard Edition: https://github.com/symfony/symfony-standard/ diff --git a/contributing/code/security.rst b/contributing/code/security.rst index f523616167c..ab4cc402b1c 100644 --- a/contributing/code/security.rst +++ b/contributing/code/security.rst @@ -96,36 +96,36 @@ Security Advisories This section indexes security vulnerabilities that were fixed in Symfony releases, starting from Symfony 1.0.0: -* May 26, 2015: `CVE-2015-4050: ESI unauthorized access `_ (Symfony 2.3.29, 2.5.12 and 2.6.8) -* April 1, 2015: `CVE-2015-2309: Unsafe methods in the Request class `_ (Symfony 2.3.27, 2.5.11 and 2.6.6) -* April 1, 2015: `CVE-2015-2308: Esi Code Injection `_ (Symfony 2.3.27, 2.5.11 and 2.6.6) -* September 3, 2014: `CVE-2014-6072: CSRF vulnerability in the Web Profiler `_ (Symfony 2.3.19, 2.4.9 and 2.5.4) -* September 3, 2014: `CVE-2014-6061: Security issue when parsing the Authorization header `_ (Symfony 2.3.19, 2.4.9 and 2.5.4) -* September 3, 2014: `CVE-2014-5245: Direct access of ESI URLs behind a trusted proxy `_ (Symfony 2.3.19, 2.4.9 and 2.5.4) -* September 3, 2014: `CVE-2014-5244: Denial of service with a malicious HTTP Host header `_ (Symfony 2.3.19, 2.4.9 and 2.5.4) -* July 15, 2014: `Security releases: Symfony 2.3.18, 2.4.8, and 2.5.2 released `_ (`CVE-2014-4931 `_) -* October 10, 2013: `Security releases: Symfony 2.0.25, 2.1.13, 2.2.9, and 2.3.6 released `_ (`CVE-2013-5958 `_) -* August 7, 2013: `Security releases: Symfony 2.0.24, 2.1.12, 2.2.5, and 2.3.3 released `_ (`CVE-2013-4751 `_ and `CVE-2013-4752 `_) -* January 17, 2013: `Security release: Symfony 2.0.22 and 2.1.7 released `_ (`CVE-2013-1348 `_ and `CVE-2013-1397 `_) -* December 20, 2012: `Security release: Symfony 2.0.20 and 2.1.5 `_ (`CVE-2012-6431 `_ and `CVE-2012-6432 `_) -* November 29, 2012: `Security release: Symfony 2.0.19 and 2.1.4 `_ -* November 25, 2012: `Security release: symfony 1.4.20 released `_ (`CVE-2012-5574 `_) -* August 28, 2012: `Security Release: Symfony 2.0.17 released `_ -* May 30, 2012: `Security Release: symfony 1.4.18 released `_ (`CVE-2012-2667 `_) -* February 24, 2012: `Security Release: Symfony 2.0.11 released `_ -* November 16, 2011: `Security Release: Symfony 2.0.6 `_ -* March 21, 2011: `symfony 1.3.10 and 1.4.10: security releases `_ -* June 29, 2010: `Security Release: symfony 1.3.6 and 1.4.6 `_ -* May 31, 2010: `symfony 1.3.5 and 1.4.5 `_ -* February 25, 2010: `Security Release: 1.2.12, 1.3.3 and 1.4.3 `_ -* February 13, 2010: `symfony 1.3.2 and 1.4.2 `_ -* April 27, 2009: `symfony 1.2.6: Security fix `_ -* October 03, 2008: `symfony 1.1.4 released: Security fix `_ -* May 14, 2008: `symfony 1.0.16 is out `_ -* April 01, 2008: `symfony 1.0.13 is out `_ -* March 21, 2008: `symfony 1.0.12 is (finally) out ! `_ -* June 25, 2007: `symfony 1.0.5 released (security fix) `_ +* May 26, 2015: `CVE-2015-4050: ESI unauthorized access `_ (Symfony 2.3.29, 2.5.12 and 2.6.8) +* April 1, 2015: `CVE-2015-2309: Unsafe methods in the Request class `_ (Symfony 2.3.27, 2.5.11 and 2.6.6) +* April 1, 2015: `CVE-2015-2308: Esi Code Injection `_ (Symfony 2.3.27, 2.5.11 and 2.6.6) +* September 3, 2014: `CVE-2014-6072: CSRF vulnerability in the Web Profiler `_ (Symfony 2.3.19, 2.4.9 and 2.5.4) +* September 3, 2014: `CVE-2014-6061: Security issue when parsing the Authorization header `_ (Symfony 2.3.19, 2.4.9 and 2.5.4) +* September 3, 2014: `CVE-2014-5245: Direct access of ESI URLs behind a trusted proxy `_ (Symfony 2.3.19, 2.4.9 and 2.5.4) +* September 3, 2014: `CVE-2014-5244: Denial of service with a malicious HTTP Host header `_ (Symfony 2.3.19, 2.4.9 and 2.5.4) +* July 15, 2014: `Security releases: Symfony 2.3.18, 2.4.8, and 2.5.2 released `_ (`CVE-2014-4931 `_) +* October 10, 2013: `Security releases: Symfony 2.0.25, 2.1.13, 2.2.9, and 2.3.6 released `_ (`CVE-2013-5958 `_) +* August 7, 2013: `Security releases: Symfony 2.0.24, 2.1.12, 2.2.5, and 2.3.3 released `_ (`CVE-2013-4751 `_ and `CVE-2013-4752 `_) +* January 17, 2013: `Security release: Symfony 2.0.22 and 2.1.7 released `_ (`CVE-2013-1348 `_ and `CVE-2013-1397 `_) +* December 20, 2012: `Security release: Symfony 2.0.20 and 2.1.5 `_ (`CVE-2012-6431 `_ and `CVE-2012-6432 `_) +* November 29, 2012: `Security release: Symfony 2.0.19 and 2.1.4 `_ +* November 25, 2012: `Security release: symfony 1.4.20 released `_ (`CVE-2012-5574 `_) +* August 28, 2012: `Security Release: Symfony 2.0.17 released `_ +* May 30, 2012: `Security Release: symfony 1.4.18 released `_ (`CVE-2012-2667 `_) +* February 24, 2012: `Security Release: Symfony 2.0.11 released `_ +* November 16, 2011: `Security Release: Symfony 2.0.6 `_ +* March 21, 2011: `symfony 1.3.10 and 1.4.10: security releases `_ +* June 29, 2010: `Security Release: symfony 1.3.6 and 1.4.6 `_ +* May 31, 2010: `symfony 1.3.5 and 1.4.5 `_ +* February 25, 2010: `Security Release: 1.2.12, 1.3.3 and 1.4.3 `_ +* February 13, 2010: `symfony 1.3.2 and 1.4.2 `_ +* April 27, 2009: `symfony 1.2.6: Security fix `_ +* October 03, 2008: `symfony 1.1.4 released: Security fix `_ +* May 14, 2008: `symfony 1.0.16 is out `_ +* April 01, 2008: `symfony 1.0.13 is out `_ +* March 21, 2008: `symfony 1.0.12 is (finally) out ! `_ +* June 25, 2007: `symfony 1.0.5 released (security fix) `_ .. _Git repository: https://github.com/symfony/symfony -.. _blog: http://symfony.com/blog/ -.. _Security Advisories: http://symfony.com/blog/category/security-advisories +.. _blog: https://symfony.com/blog/ +.. _Security Advisories: https://symfony.com/blog/category/security-advisories diff --git a/contributing/community/releases.rst b/contributing/community/releases.rst index 9088472abb4..3e46cbcb667 100644 --- a/contributing/community/releases.rst +++ b/contributing/community/releases.rst @@ -114,7 +114,7 @@ Version Feature Freeze Release End of Maintenance End of Life If you want to learn more about the timeline of any given Symfony version, use the online `timeline calculator`_. You can also get all data as a JSON - string via a URL like `http://symfony.com/roadmap.json?version=2.x`. + string via a URL like `https://symfony.com/roadmap.json?version=2.x`. .. tip:: @@ -178,6 +178,6 @@ version is published every two years and there is a year to upgrade. .. _Semantic Versioning: http://semver.org/ .. _Git repository: https://github.com/symfony/symfony .. _SensioLabs: http://sensiolabs.com/ -.. _roadmap notification: http://symfony.com/roadmap -.. _extended to September 2014: http://symfony.com/blog/extended-maintenance-for-symfony-2-4 -.. _timeline calculator: http://symfony.com/roadmap +.. _roadmap notification: https://symfony.com/roadmap +.. _extended to September 2014: https://symfony.com/blog/extended-maintenance-for-symfony-2-4 +.. _timeline calculator: https://symfony.com/roadmap diff --git a/contributing/documentation/overview.rst b/contributing/documentation/overview.rst index f7cd0608926..984275d6c61 100644 --- a/contributing/documentation/overview.rst +++ b/contributing/documentation/overview.rst @@ -320,7 +320,7 @@ definitely don't want you to waste your time! .. _reStructuredText: http://docutils.sourceforge.net/rst.html .. _GitHub: https://github.com/ .. _`fork the repository`: https://help.github.com/articles/fork-a-repo -.. _`Symfony Documentation Contributors`: http://symfony.com/contributors/doc +.. _`Symfony Documentation Contributors`: https://symfony.com/contributors/doc .. _SensioLabsConnect: https://connect.sensiolabs.com/ .. _`Symfony Documentation Badge`: https://connect.sensiolabs.com/badge/36/symfony-documentation-contributor .. _`sync your fork`: https://help.github.com/articles/syncing-a-fork diff --git a/contributing/documentation/standards.rst b/contributing/documentation/standards.rst index 3210ee65927..2079a0d7e8b 100644 --- a/contributing/documentation/standards.rst +++ b/contributing/documentation/standards.rst @@ -44,7 +44,7 @@ Example echo 'You cannot use the :: shortcut here'; - .. _`Symfony Documentation`: http://symfony.com/doc + .. _`Symfony Documentation`: https://symfony.com/doc Code Examples ------------- diff --git a/cookbook/controller/error_pages.rst b/cookbook/controller/error_pages.rst index 20112046b6f..a76fd767d24 100644 --- a/cookbook/controller/error_pages.rst +++ b/cookbook/controller/error_pages.rst @@ -9,9 +9,9 @@ In Symfony applications, all errors are treated as exceptions, no matter if they are just a 404 Not Found error or a fatal error triggered by throwing some exception in your code. -In the `development environment`_, Symfony catches all the exceptions and displays -a special **exception page** with lots of debug information to help you quickly -discover the root problem: +In the :doc:`development environment `, +Symfony catches all the exceptions and displays a special **exception page** +with lots of debug information to help you quickly discover the root problem: .. image:: /images/cookbook/controller/error_pages/exceptions-in-dev-environment.png :alt: A typical exception page in the development environment @@ -259,5 +259,4 @@ time and again, you can have just one (or several) listeners deal with them. and takes measures like redirecting the user to the login page, logging them out and other things. -.. _`development environment`: http://symfony.com/doc/current/cookbook/configuration/environments.html .. _`WebfactoryExceptionsBundle`: https://github.com/webfactory/exceptions-bundle diff --git a/cookbook/controller/service.rst b/cookbook/controller/service.rst index 4fa3af4deb5..00dfb2036c4 100644 --- a/cookbook/controller/service.rst +++ b/cookbook/controller/service.rst @@ -327,4 +327,4 @@ controller: .. _`Controller class source code`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php .. _`base Controller class`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php -.. _`FrameworkExtraBundle documentation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/routing.html +.. _`FrameworkExtraBundle documentation`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/routing.html diff --git a/cookbook/doctrine/registration_form.rst b/cookbook/doctrine/registration_form.rst index 0c64e4cf7e3..4cfbd1bc88a 100644 --- a/cookbook/doctrine/registration_form.rst +++ b/cookbook/doctrine/registration_form.rst @@ -364,4 +364,4 @@ object to the database. The extra ``terms`` checkbox on the ``Registration`` model class is used during validation, but not actually used afterwards when saving the User to the database. -.. _`CVE-2013-5750`: http://symfony.com/blog/cve-2013-5750-security-issue-in-fosuserbundle-login-form +.. _`CVE-2013-5750`: https://symfony.com/blog/cve-2013-5750-security-issue-in-fosuserbundle-login-form diff --git a/cookbook/security/entity_provider.rst b/cookbook/security/entity_provider.rst index 90c55eaf5dc..6c760ef7dbf 100644 --- a/cookbook/security/entity_provider.rst +++ b/cookbook/security/entity_provider.rst @@ -240,7 +240,7 @@ the username and then check the password (more on passwords in a moment): - + @@ -474,7 +474,7 @@ For more details on these methods, see :class:`Symfony\\Component\\Security\\Cor .. tip:: - Don't forget to add the repository class to the + Don't forget to add the repository class to the :ref:`mapping definition of your entity `. To finish this, just remove the ``property`` key from the user provider in @@ -566,5 +566,5 @@ or worry about it. In Symfony 2.1, the ``equals`` method was removed from ``UserInterface`` and the ``EquatableInterface`` was introduced in its place. -.. _fixtures: http://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html +.. _fixtures: https://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html .. _FOSUserBundle: https://github.com/FriendsOfSymfony/FOSUserBundle diff --git a/cookbook/templating/PHP.rst b/cookbook/templating/PHP.rst index 56c47915743..827516031dc 100644 --- a/cookbook/templating/PHP.rst +++ b/cookbook/templating/PHP.rst @@ -353,4 +353,4 @@ instance, to output something in a JavaScript script, use the ``js`` context:: escape($var, 'js') ?> -.. _`@Template`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/view` +.. _`@Template`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/view` diff --git a/cookbook/workflow/new_project_git.rst b/cookbook/workflow/new_project_git.rst index 66a5a251761..56043493b81 100644 --- a/cookbook/workflow/new_project_git.rst +++ b/cookbook/workflow/new_project_git.rst @@ -94,7 +94,7 @@ a `barebones repository`_ and then pushing to it. One library that helps manage this is `Gitolite`_. .. _`Git`: http://git-scm.com/ -.. _`Symfony Standard Edition`: http://symfony.com/download +.. _`Symfony Standard Edition`: https://symfony.com/download .. _`git submodules`: http://git-scm.com/book/en/Git-Tools-Submodules .. _`GitHub`: https://github.com/ .. _`barebones repository`: http://git-scm.com/book/en/Git-Basics-Getting-a-Git-Repository diff --git a/cookbook/workflow/new_project_svn.rst b/cookbook/workflow/new_project_svn.rst index 934faa4ce2a..8fe7e9eacd9 100644 --- a/cookbook/workflow/new_project_svn.rst +++ b/cookbook/workflow/new_project_svn.rst @@ -135,7 +135,7 @@ central repository to work. You then have several solutions: .. _`Git`: http://git-scm.com/ .. _`SVN`: http://subversion.apache.org/ .. _`Subversion`: http://subversion.apache.org/ -.. _`Symfony Standard Edition`: http://symfony.com/download +.. _`Symfony Standard Edition`: https://symfony.com/download .. _`Version Control with Subversion`: http://svnbook.red-bean.com/ .. _`GitHub`: https://github.com/ .. _`Google code`: http://code.google.com/hosting/ diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index d2fdb146b5a..75af745849a 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -1527,6 +1527,6 @@ Full Default Configuration .. _`protocol-relative`: http://tools.ietf.org/html/rfc3986#section-4.2 .. _`HTTP Host header attacks`: http://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html -.. _`Security Advisory Blog post`: http://symfony.com/blog/security-releases-symfony-2-0-24-2-1-12-2-2-5-and-2-3-3-released#cve-2013-4752-request-gethost-poisoning +.. _`Security Advisory Blog post`: https://symfony.com/blog/security-releases-symfony-2-0-24-2-1-12-2-2-5-and-2-3-3-released#cve-2013-4752-request-gethost-poisoning .. _`Doctrine Cache`: http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/caching.html .. _`PhpStormProtocol`: https://github.com/aik099/PhpStormProtocol From 968cb659a0f9bcbc9a538dd12739160ed46132de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Marint=C5=A1enko?= Date: Tue, 23 Sep 2014 07:53:08 +0300 Subject: [PATCH 0263/2667] add & update doc entries on AbstractVoter implementation --- cookbook/security/abstract_voter.rst.inc | 31 ++++ cookbook/security/voters.rst | 7 + cookbook/security/voters_data_permission.rst | 141 +++++++++---------- 3 files changed, 101 insertions(+), 78 deletions(-) create mode 100644 cookbook/security/abstract_voter.rst.inc diff --git a/cookbook/security/abstract_voter.rst.inc b/cookbook/security/abstract_voter.rst.inc new file mode 100644 index 00000000000..f12d1d9619a --- /dev/null +++ b/cookbook/security/abstract_voter.rst.inc @@ -0,0 +1,31 @@ +.. code-block:: php + + abstract class AbstractVoter implements VoterInterface + { + public function supportsAttribute($attribute); + public function supportsClass($class); + public function vote(TokenInterface $token, $object, array $attributes); + + abstract protected function getSupportedClasses(); + abstract protected function getSupportedAttributes(); + abstract protected function isGranted($attribute, $object, $user = null); + } + +Behind the scenes this class implements the +:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`, +which has this structure: + +.. include:: /cookbook/security/voter_interface.rst.inc + +The basic functionality covering common use cases is provided +and end developer is expected to implement the abstract methods. + +The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::getSupportedClasses` +method is used to provide an array of supported classes, i.e. ['\Acme\DemoBundle\Model\Product'] + +The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::getSupportedAttributes` +method is used to provide an array of supported attributes, i.e. ['CREATE', 'READ'] + +The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::isGranted` +method must implement the business logic that verifies whether or not a given +user is allowed a given attribute on a given object. This method must return a boolean. \ No newline at end of file diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index 835d15e409b..b343482ed05 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -92,6 +92,13 @@ the security layer. This can be done easily through the service container. methods in your implementation of the ``vote()`` method and return ``ACCESS_ABSTAIN`` if your voter does not support the class or attribute. + +.. tip:: + + An + :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter` + is provided to cover the common use cases when implementing security voters. + Declaring the Voter as a Service -------------------------------- diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index e17565ea218..27601c7bc82 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -25,7 +25,7 @@ How Symfony Uses Voters In order to use voters, you have to understand how Symfony works with them. All voters are called each time you use the ``isGranted()`` method on Symfony's -authorization checker (i.e. the ``security.authorization_checker`` service). Each +authorization checker (i.e. the ``security.authorization_checker`` service). Each one decides if the current user should have access to some resource. Ultimately, Symfony uses one of three different approaches on what to do @@ -37,11 +37,19 @@ For more information take a look at The Voter Interface ------------------- -A custom voter must implement -:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`, -which has this structure: +A custom voter needs to implement +:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface` +or extend :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter`, +which makes creating a voter even easier. -.. include:: /cookbook/security/voter_interface.rst.inc +.. code-block:: php + + abstract class AbstractVoter implements VoterInterface + { + abstract protected function getSupportedClasses(); + abstract protected function getSupportedAttributes(); + abstract protected function isGranted($attribute, $object, $user = null); + } In this example, the voter will check if the user has access to a specific object according to your custom conditions (e.g. they must be the owner of @@ -61,90 +69,74 @@ edit a particular object. Here's an example implementation: // src/AppBundle/Security/Authorization/Voter/PostVoter.php namespace AppBundle\Security\Authorization\Voter; - use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; - use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter; use Symfony\Component\Security\Core\User\UserInterface; - class PostVoter implements VoterInterface + class PostVoter extends AbstractVoter { const VIEW = 'view'; const EDIT = 'edit'; - public function supportsAttribute($attribute) + protected function getSupportedAttributes() { - return in_array($attribute, array( - self::VIEW, - self::EDIT, - )); + return array(self::VIEW, self::EDIT); } - public function supportsClass($class) + protected function getSupportedClasses() { - $supportedClass = 'AppBundle\Entity\Post'; - - return $supportedClass === $class || is_subclass_of($class, $supportedClass); + return array('AppBundle\Entity\Post'); } - /** - * @var \AppBundle\Entity\Post $post - */ - public function vote(TokenInterface $token, $post, array $attributes) + protected function isGranted($attribute, $post, $user = null) { - // check if class of this object is supported by this voter - if (!$this->supportsClass(get_class($post))) { - return VoterInterface::ACCESS_ABSTAIN; - } - - // check if the voter is used correct, only allow one attribute - // this isn't a requirement, it's just one easy way for you to - // design your voter - if (1 !== count($attributes)) { - throw new \InvalidArgumentException( - 'Only one attribute is allowed for VIEW or EDIT' - ); - } - - // set the attribute to check against - $attribute = $attributes[0]; - - // check if the given attribute is covered by this voter - if (!$this->supportsAttribute($attribute)) { - return VoterInterface::ACCESS_ABSTAIN; - } - - // get current logged in user - $user = $token->getUser(); - // make sure there is a user object (i.e. that the user is logged in) if (!$user instanceof UserInterface) { - return VoterInterface::ACCESS_DENIED; + return false; + } + + // the data object could have for example a method isPrivate() + // which checks the Boolean attribute $private + if ($attribute == self::VIEW && !$post->isPrivate()) { + return true; } - switch($attribute) { - case self::VIEW: - // the data object could have for example a method isPrivate() - // which checks the boolean attribute $private - if (!$post->isPrivate()) { - return VoterInterface::ACCESS_GRANTED; - } - break; - - case self::EDIT: - // we assume that our data object has a method getOwner() to - // get the current owner user entity for this data object - if ($user->getId() === $post->getOwner()->getId()) { - return VoterInterface::ACCESS_GRANTED; - } - break; + // we assume that our data object has a method getOwner() to + // get the current owner user entity for this data object + if ($attribute == self::EDIT && $user->getId() === $post->getOwner()->getId()) { + return true; } - return VoterInterface::ACCESS_DENIED; + return false; } } That's it! The voter is done. The next step is to inject the voter into the security layer. +To recap, here's what's expected from the three abstract methods: + +:method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::getSupportedClasses` + It tells Symfony that your voter should be called whenever an object of one + of the given classes is passed to ``isGranted()`` For example, if you return + ``array('AppBundle\Model\Product')``, Symfony will call your voter when a + ``Product`` object is passed to ``isGranted()``. + +:method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::getSupportedAttributes` + It tells Symfony that your voter should be called whenever one of these + strings is passes as the first argument to ``isGranted()``. For example, if + you return ``array('CREATE', 'READ')``, then Symfony will call your voter + when one of these is passed to ``isGranted()``. + +:method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::isGranted` + It implements the business logic that verifies whether or not a given user is + allowed access to a given attribute (e.g. ``CREATE`` or ``READ``) on a given + object. This method must return a boolean. + +.. note:: + + Currently, to use the ``AbstractVoter`` base class, you must be creating a + voter where an object is always passed to ``isGranted()``. + Declaring the Voter as a Service -------------------------------- @@ -203,6 +195,7 @@ from the authorization checker is called. use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\Security\Core\Exception\AccessDeniedException; class PostController extends Controller { @@ -212,25 +205,17 @@ from the authorization checker is called. $post = ...; // keep in mind, this will call all registered security voters - $this->denyAccessUnlessGranted('view', $post, 'Unauthorized access!'); - - // the equivalent code without using the denyAccessUnlessGranted() shortcut - // use Symfony\Component\Security\Core\Exception\AccessDeniedException; - // - // if (false === $this->get('security.authorization_checker')->isGranted('view', $post)) { - // throw new AccessDeniedException('Unauthorized access!'); - // } + if (false === $this->get('security.authorization_checker')->isGranted('view', $post)) { + throw new AccessDeniedException('Unauthorised access!'); + } return new Response('

'.$post->getName().'

'); } } .. versionadded:: 2.6 - The ``security.authorization_checker`` service was introduced in Symfony 2.6. Prior - to Symfony 2.6, you had to use the ``isGranted()`` method of the ``security.context`` service. - -.. versionadded:: 2.6 - The ``denyAccessUnlessGranted()`` method was introduced in Symfony 2.6 as a shortcut. - It uses ``security.authorization_checker`` and throws an ``AccessDeniedException`` if needed. + The ``security.authorization_checker`` service was introduced in Symfony 2.6. + Prior to Symfony 2.6, you had to use the ``isGranted()`` method of the + ``security.context`` service. It's that easy! From e9053c04be8b86a34f43070c15e25b635b332673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Marint=C5=A1enko?= Date: Tue, 30 Sep 2014 07:54:27 +0300 Subject: [PATCH 0264/2667] fix problems pointed out by @javiereguiluz and @cordoval --- cookbook/security/abstract_voter.rst.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/security/abstract_voter.rst.inc b/cookbook/security/abstract_voter.rst.inc index f12d1d9619a..dffd90b4a00 100644 --- a/cookbook/security/abstract_voter.rst.inc +++ b/cookbook/security/abstract_voter.rst.inc @@ -18,7 +18,7 @@ which has this structure: .. include:: /cookbook/security/voter_interface.rst.inc The basic functionality covering common use cases is provided -and end developer is expected to implement the abstract methods. +and developer is expected to implement the abstract methods. The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::getSupportedClasses` method is used to provide an array of supported classes, i.e. ['\Acme\DemoBundle\Model\Product'] @@ -28,4 +28,4 @@ method is used to provide an array of supported attributes, i.e. ['CREATE', 'REA The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::isGranted` method must implement the business logic that verifies whether or not a given -user is allowed a given attribute on a given object. This method must return a boolean. \ No newline at end of file +user is allowed access to a given attribute on a given object. This method must return a boolean. From 59c60b1cdff78e0886393b5d704bab143c9ed89b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Marint=C5=A1enko?= Date: Sat, 31 Jan 2015 11:13:35 +0200 Subject: [PATCH 0265/2667] add fixes to abstract_voter include file --- cookbook/security/abstract_voter.rst.inc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cookbook/security/abstract_voter.rst.inc b/cookbook/security/abstract_voter.rst.inc index dffd90b4a00..3f1319bd01b 100644 --- a/cookbook/security/abstract_voter.rst.inc +++ b/cookbook/security/abstract_voter.rst.inc @@ -21,10 +21,14 @@ The basic functionality covering common use cases is provided and developer is expected to implement the abstract methods. The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::getSupportedClasses` -method is used to provide an array of supported classes, i.e. ['\Acme\DemoBundle\Model\Product'] +method tells Symfony that your voter should be called whenever an object of one of the given classes +is passed to `isGranted` For example, if you return ['\Acme\DemoBundle\Model\Product'], +Symfony will call your voter when a `Product` object is passed to `isGranted`. The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::getSupportedAttributes` -method is used to provide an array of supported attributes, i.e. ['CREATE', 'READ'] +method tells Symfony that your voter should be called whenever one of these strings is passes as the +first argument to `isGranted`. For example, if you return `array('CREATE', 'READ')`, then +Symfony will call your voter when one of these is passed to `isGranted`. The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::isGranted` method must implement the business logic that verifies whether or not a given From 60643f005be393340e564b277ba9e08b30f200d1 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 22 Jun 2015 13:30:59 +0200 Subject: [PATCH 0266/2667] Removed the abstract_voter.rst.inc file --- cookbook/security/abstract_voter.rst.inc | 35 ------------------------ 1 file changed, 35 deletions(-) delete mode 100644 cookbook/security/abstract_voter.rst.inc diff --git a/cookbook/security/abstract_voter.rst.inc b/cookbook/security/abstract_voter.rst.inc deleted file mode 100644 index 3f1319bd01b..00000000000 --- a/cookbook/security/abstract_voter.rst.inc +++ /dev/null @@ -1,35 +0,0 @@ -.. code-block:: php - - abstract class AbstractVoter implements VoterInterface - { - public function supportsAttribute($attribute); - public function supportsClass($class); - public function vote(TokenInterface $token, $object, array $attributes); - - abstract protected function getSupportedClasses(); - abstract protected function getSupportedAttributes(); - abstract protected function isGranted($attribute, $object, $user = null); - } - -Behind the scenes this class implements the -:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`, -which has this structure: - -.. include:: /cookbook/security/voter_interface.rst.inc - -The basic functionality covering common use cases is provided -and developer is expected to implement the abstract methods. - -The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::getSupportedClasses` -method tells Symfony that your voter should be called whenever an object of one of the given classes -is passed to `isGranted` For example, if you return ['\Acme\DemoBundle\Model\Product'], -Symfony will call your voter when a `Product` object is passed to `isGranted`. - -The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::getSupportedAttributes` -method tells Symfony that your voter should be called whenever one of these strings is passes as the -first argument to `isGranted`. For example, if you return `array('CREATE', 'READ')`, then -Symfony will call your voter when one of these is passed to `isGranted`. - -The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::isGranted` -method must implement the business logic that verifies whether or not a given -user is allowed access to a given attribute on a given object. This method must return a boolean. From 73bd9082fe3c15aedd16a3ed457e5bb053190855 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 23 Jun 2015 10:08:05 +0200 Subject: [PATCH 0267/2667] Fixed some typos --- cookbook/security/voters.rst | 5 ++--- cookbook/security/voters_data_permission.rst | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index b343482ed05..531971025e5 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -92,7 +92,6 @@ the security layer. This can be done easily through the service container. methods in your implementation of the ``vote()`` method and return ``ACCESS_ABSTAIN`` if your voter does not support the class or attribute. - .. tip:: An @@ -204,8 +203,8 @@ application configuration file with the following code. That's it! Now, when deciding whether or not a user should have access, the new voter will deny access to any user in the list of blacklisted IPs. -Note that the voters are only called, if any access is actually checked. So -you need at least something like +Note that the voters are only called, if any access is actually checked. So +you need at least something like .. configuration-block:: diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 27601c7bc82..0f6ac1a0d3d 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -117,13 +117,13 @@ To recap, here's what's expected from the three abstract methods: :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::getSupportedClasses` It tells Symfony that your voter should be called whenever an object of one - of the given classes is passed to ``isGranted()`` For example, if you return + of the given classes is passed to ``isGranted()``. For exam 10000 ple, if you return ``array('AppBundle\Model\Product')``, Symfony will call your voter when a ``Product`` object is passed to ``isGranted()``. :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter::getSupportedAttributes` It tells Symfony that your voter should be called whenever one of these - strings is passes as the first argument to ``isGranted()``. For example, if + strings is passed as the first argument to ``isGranted()``. For example, if you return ``array('CREATE', 'READ')``, then Symfony will call your voter when one of these is passed to ``isGranted()``. From 95537d3c68fca280d15d46b55cdba7d04f7f66f6 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 24 Jun 2015 08:58:41 +0200 Subject: [PATCH 0268/2667] Added a link to the AbstractVoter cookbook --- cookbook/security/voters.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index 531971025e5..df575951f0e 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -94,9 +94,8 @@ the security layer. This can be done easily through the service container. .. tip:: - An - :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AbstractVoter` - is provided to cover the common use cases when implementing security voters. + An :doc:`AbstractVoter ` is + provided to cover the common use cases when implementing security voters. Declaring the Voter as a Service -------------------------------- From e46f02e2fe8c0bec539f3736d7ee5ef371c2102c Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 30 Jun 2015 09:11:47 -0400 Subject: [PATCH 0269/2667] [#5423] Minor tweaks to new voter update --- cookbook/security/voters_data_permission.rst | 36 ++++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 0f6ac1a0d3d..d131b259458 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -94,17 +94,23 @@ edit a particular object. Here's an example implementation: return false; } - // the data object could have for example a method isPrivate() - // which checks the Boolean attribute $private - if ($attribute == self::VIEW && !$post->isPrivate()) { - return true; - } - - // we assume that our data object has a method getOwner() to - // get the current owner user entity for this data object - if ($attribute == self::EDIT && $user->getId() === $post->getOwner()->getId()) { - return true; - } + switch($attribute) { + case self::VIEW: + // the data object could have for example a method isPrivate() + // which checks the Boolean attribute $private + if (!$post->isPrivate()) { + return true; + } + + break; + case self::EDIT: + // we assume that our data object has a method getOwner() to + // get the current owner user entity for this data object + if ($user->getId() === $post->getOwner()->getId()) { + return true; + } + + break; return false; } @@ -195,7 +201,6 @@ from the authorization checker is called. use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Response; - use Symfony\Component\Security\Core\Exception\AccessDeniedException; class PostController extends Controller { @@ -204,9 +209,10 @@ from the authorization checker is called. // get a Post instance $post = ...; - // keep in mind, this will call all registered security voters - if (false === $this->get('security.authorization_checker')->isGranted('view', $post)) { - throw new AccessDeniedException('Unauthorised access!'); + $authChecker = $this->get('security.authorization_checker'); + + if (false === $authChecker->isGranted('view', $post)) { + throw $this->createAccessDeniedException('Unauthorized access!'); } return new Response('

'.$post->getName().'

'); From 4d0f6ea1bcb2e197c388d822698c39bd66ebabe5 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 30 Jun 2015 16:04:27 +0200 Subject: [PATCH 0270/2667] Minor fixes --- reference/dic_tags.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 20794ff3319..ea752f9d6c1 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -337,10 +337,10 @@ wrapping their names with ``%`` characters). .. note:: - When using the ``auto_alias`` tag is not mandatory to define the aliased + When using the ``auto_alias`` tag it's not mandatory to define the aliased services as private. However, doing that (like in the above example) makes sense most of the times to prevent accessing those services directly instead - of using the generic service. + of using the generic service alias. console.command --------------- From 1cff82669740d020f2ed86cb6155870cf8ffafdb Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 30 Jun 2015 16:11:52 +0200 Subject: [PATCH 0271/2667] Fixed a minor typo --- cookbook/cache/varnish.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/cache/varnish.rst b/cookbook/cache/varnish.rst index 7df7f710abd..39b686872f5 100644 --- a/cookbook/cache/varnish.rst +++ b/cookbook/cache/varnish.rst @@ -191,7 +191,7 @@ Symfony adds automatically: .. tip:: If you followed the advice about ensuring a consistent caching - behaviour, those VCL functions already exist. Just append the code + behavior, those VCL functions already exist. Just append the code to the end of the function, they won't interfere with each other. .. index:: From 9cf3b1e690aa73bc337ca826a5954e2672285914 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Wed, 1 Jul 2015 09:38:33 +0200 Subject: [PATCH 0272/2667] Added June changelog --- changelog.rst | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/changelog.rst b/changelog.rst index 57bb9e95bb7..75591224909 100644 --- a/changelog.rst +++ b/changelog.rst @@ -13,6 +13,84 @@ documentation. Do you also want to participate in the Symfony Documentation? Take a look at the ":doc:`/contributing/documentation/overview`" article. +June, 2015 +---------- + +New Documentation +~~~~~~~~~~~~~~~~~ + +* `#5401 `_ Added some more docs about the remember me feature (WouterJ) +* `#5382 `_ Added support for standard Forwarded header (tony-co, javiereguiluz) +* `#5361 `_ Document security.switch_user event (Rvanlaak) +* `#5313 `_ Documented the overridden form options (javiereguiluz) +* `#5307 `_ Update data_transformers.rst (zebba) +* `#5186 `_ Added a new article about using/installing unstable Symfony versions (javiereguiluz) +* `#5166 `_ Proposed a new article about using pure PHP libraries with Assetic (javiereguiluz) +* `#5014 `_ Updated the best practices article for reusable bundles (javiereguiluz) +* `#5368 `_ added examples for squashing (OskarStark) +* `#5428 `_ Improved description of choice_list option (adamziel, javiereguiluz) +* `#5336 `_ Adding a paragraph about updating multiple packages during an update (weaverryan) +* `#5375 `_ Added a new cookbook about file uploading (javiereguiluz) +* `#5385 `_ Added a note about the need to require Composer's autoload file (javiereguiluz) +* `#5386 `_ Re-write of Page Creation (weaverryan) +* `#5355 `_ Added a mention to the Symfony Demo application (javiereguiluz) +* `#5373 `_ Added mentions to some popular (and useful) Symfony bundles (javiereguiluz) + +Fixed Documentation +~~~~~~~~~~~~~~~~~~~ + +* `#5407 `_ Change PhpStormOpener to PhpStormProtocol (King2500) +* `#5454 `_ Changed dump() to var_dump() (WouterJ) +* `#5417 `_ Add use statement for InputDefinition (harikt) +* `#5420 `_ Fix invalid method name (bocharsky-bw) +* `#5431 `_ Updated the code to display flash messages (aykin, javiereguiluz) +* `#5438 `_ Fixed 404 at Configuring Sessions and Save Handlers (2.3 branch) (suzuki) +* `#5397 `_ Escape backslash in error message (WouterJ) +* `#5379 `_ [Cookbook][Console] don't use BufferedOutput on Symfony 2.3 (xabbuh) +* `#5350 `_ [Form][2.3] fix `validation_groups` typos (craue) +* `#5356 `_ [Form] Fixed typo about _token field name for CSRF protection (JMLamodiere) +* `#5362 `_ Fix invalid endtag (norkunas) + +Minor Documentation Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* `#5467 `_ use HTTPS for links to symfony.com (xabbuh) +* `#5466 `_ data transformers cookbook service definition typo (intrepion) +* `#5414 `_ Rewrite sentence about fingers crossed handler action level (snoek09) +* `#5402 `_ [Contributing] [Standards] Added entry for Yoda conditions (phansys) +* `#5369 `_ Remove the Propel book chapter and explain why we do that (javiereguiluz) +* `#5457 `_ [Cookbook][Assetic] complete a sentence (xabbuh) +* `#5398 `_ Quick review of the remember me article (WouterJ) +* `#5399 `_ Quick review of Form login chapter (WouterJ) +* `#5403 `_ [Contributing] [Standards] Added entry for identical comparison (phansys) +* `#5378 `_ [Cookbook][Controller] use the jinja lexer to render Twig code (xabbuh) +* `#5421 `_ Update the name of the branch for new BC features (Restless-ET) +* `#5441 `_ [Contributing] remove mailing list and forum references (xabbuh) +* `#5433 `_ Warn users of older PHP versions Crawler might not decode HTML entities properly (jakzal, javiereguiluz) +* `#5293 `_ [Translation] Add note about how to override translation in chi… (zebba) +* `#5290 `_ Overriding 3rd party bundles (anacicconi) +* `#5242 `_ Update load_balancer_reverse_proxy.rst (urg) +* `#5381 `_ remove Yoda condition (greg0ire) +* `#5451 `_ [#5388] change echo and print in examples (snoek09) +* `#5432 `_ removed squashing stuff. fixes #5368 (OskarStark) +* `#5383 `_ Reword a paragraph about service configurations (richardudovich) +* `#5389 `_ Updates to security.rst (HexTitan) +* `#5408 `_ typo (larsborn) +* `#5406 `_ Update yaml_format.rst (marcel-burkhard) +* `#5396 `_ [Cookbook][Bundles] fix a typo (xabbuh) +* `#5288 `_ Constraints - empty strings and null values (anacicconi) +* `#5284 `_ Split advanced container configuration article (WouterJ) +* `#5342 `_ [Cookbook][Bundles] clarify bundle installation instructions (xabbuh) +* `#5095 `_ Reviewed the Bundles cookbook articles (javiereguiluz) +* `#5365 `_ Finish #4967: Code style standardization on form type options (mimol91) +* `#5034 `_ Update the_big_picture.rst (oldskool) +* `#5351 `_ [Finder] minor CS fix (dunglas) +* `#5344 `_ [Book] Finish #4776 and #4782 (ifdattic) +* `#5348 `_ Fix list format (bicpi) +* `#5357 `_ [Form] Replace deprecated form_enctype by form_start (JMLamodiere) +* `#5359 `_ Bumped version of proxy manager to stable release (peterrehm) + + May, 2015 --------- From 67e02fac0348bc836f0f443f4d43c313c89265cd Mon Sep 17 00:00:00 2001 From: WouterJ Date: Wed, 1 Jul 2015 09:39:09 +0200 Subject: [PATCH 0273/2667] Added June changelog --- changelog.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/changelog.rst b/changelog.rst index d9db6484894..886b168f409 100644 --- a/changelog.rst +++ b/changelog.rst @@ -19,7 +19,9 @@ June, 2015 New Documentation ~~~~~~~~~~~~~~~~~ +* `#5423 `_ [Security] add & update doc entries on AbstractVoter implementation (Inoryy, javiereguiluz) * `#5401 `_ Added some more docs about the remember me feature (WouterJ) +* `#5384 `_ Added information about the new date handling in the comparison constraints and Range (webmozart, javiereguiluz) * `#5382 `_ Added support for standard Forwarded header (tony-co, javiereguiluz) * `#5361 `_ Document security.switch_user event (Rvanlaak) * `#5313 `_ Documented the overridden form options (javiereguiluz) @@ -27,6 +29,7 @@ New Documentation * `#5186 `_ Added a new article about using/installing unstable Symfony versions (javiereguiluz) * `#5166 `_ Proposed a new article about using pure PHP libraries with Assetic (javiereguiluz) * `#5014 `_ Updated the best practices article for reusable bundles (javiereguiluz) +* `#5435 `_ Added information about the four sub-components of Security component (javiereguiluz) * `#5368 `_ added examples for squashing (OskarStark) * `#5428 `_ Improved description of choice_list option (adamziel, javiereguiluz) * `#5336 `_ Adding a paragraph about updating multiple packages during an update (weaverryan) @@ -39,15 +42,19 @@ New Documentation Fixed Documentation ~~~~~~~~~~~~~~~~~~~ +* `#5415 `_ Updating for AppBundle and purposefully \*not\* doing work on configure (weaverryan) * `#5407 `_ Change PhpStormOpener to PhpStormProtocol (King2500) +* `#5450 `_ Fixing "Undefined method" error in code example (nebkam) * `#5454 `_ Changed dump() to var_dump() (WouterJ) * `#5417 `_ Add use statement for InputDefinition (harikt) * `#5420 `_ Fix invalid method name (bocharsky-bw) * `#5431 `_ Updated the code to display flash messages (aykin, javiereguiluz) +* `#5418 `_ Import Psr LogLevel (harikt) * `#5438 `_ Fixed 404 at Configuring Sessions and Save Handlers (2.3 branch) (suzuki) * `#5397 `_ Escape backslash in error message (WouterJ) * `#5379 `_ [Cookbook][Console] don't use BufferedOutput on Symfony 2.3 (xabbuh) * `#5350 `_ [Form][2.3] fix `validation_groups` typos (craue) +* `#5358 `_ Fix typo in description (martyshka) * `#5356 `_ [Form] Fixed typo about _token field name for CSRF protection (JMLamodiere) * `#5362 `_ Fix invalid endtag (norkunas) @@ -59,10 +66,12 @@ Minor Documentation Changes * `#5414 `_ Rewrite sentence about fingers crossed handler action level (snoek09) * `#5402 `_ [Contributing] [Standards] Added entry for Yoda conditions (phansys) * `#5369 `_ Remove the Propel book chapter and explain why we do that (javiereguiluz) +* `#5460 `_ Finish #5291: Bootstrap form theme and checkboxes (anacicconi, WouterJ) * `#5457 `_ [Cookbook][Assetic] complete a sentence (xabbuh) * `#5398 `_ Quick review of the remember me article (WouterJ) * `#5399 `_ Quick review of Form login chapter (WouterJ) * `#5403 `_ [Contributing] [Standards] Added entry for identical comparison (phansys) +* `#5392 `_ Wrap the table creation inside the class extending Command, so users … (harikt) * `#5378 `_ [Cookbook][Controller] use the jinja lexer to render Twig code (xabbuh) * `#5421 `_ Update the name of the branch for new BC features (Restless-ET) * `#5441 `_ [Contributing] remove mailing list and forum references (xabbuh) @@ -71,6 +80,7 @@ Minor Documentation Changes * `#5290 `_ Overriding 3rd party bundles (anacicconi) * `#5242 `_ Update load_balancer_reverse_proxy.rst (urg) * `#5381 `_ remove Yoda condition (greg0ire) +* `#5452 `_ [#5388] change echo and print in examples (snoek09) * `#5451 `_ [#5388] change echo and print in examples (snoek09) * `#5432 `_ removed squashing stuff. fixes #5368 (OskarStark) * `#5383 `_ Reword a paragraph about service configurations (richardudovich) @@ -82,6 +92,7 @@ Minor Documentation Changes * `#5284 `_ Split advanced container configuration article (WouterJ) * `#5342 `_ [Cookbook][Bundles] clarify bundle installation instructions (xabbuh) * `#5095 `_ Reviewed the Bundles cookbook articles (javiereguiluz) +* `#4947 `_ [Components][ClassLoader] remove DebugClassLoader (xabbuh) * `#5365 `_ Finish #4967: Code style standardization on form type options (mimol91) * `#5034 `_ Update the_big_picture.rst (oldskool) * `#5351 `_ [Finder] minor CS fix (dunglas) From 13e4d92e805cc2ee7eead57ab5367a83a7c1470b Mon Sep 17 00:00:00 2001 From: WouterJ Date: Wed, 1 Jul 2015 09:39:33 +0200 Subject: [PATCH 0274/2667] Added June changelog --- changelog.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/changelog.rst b/changelog.rst index c83a2045995..9444def5556 100644 --- a/changelog.rst +++ b/changelog.rst @@ -24,10 +24,13 @@ New Documentation * `#5384 `_ Added information about the new date handling in the comparison constraints and Range (webmozart, javiereguiluz) * `#5382 `_ Added support for standard Forwarded header (tony-co, javiereguiluz) * `#5361 `_ Document security.switch_user event (Rvanlaak) +* `#5332 `_ [Serializer] ObjectNormalizer, object_to_populate doc. Minor enhancements. (dunglas) +* `#5335 `_ [Serializer] Updated the cookbook. (dunglas) * `#5313 `_ Documented the overridden form options (javiereguiluz) * `#5307 `_ Update data_transformers.rst (zebba) * `#5186 `_ Added a new article about using/installing unstable Symfony versions (javiereguiluz) * `#5166 `_ Proposed a new article about using pure PHP libraries with Assetic (javiereguiluz) +* `#5416 `_ fix for Symfony 2.7 (DQNEO) * `#5014 `_ Updated the best practices article for reusable bundles (javiereguiluz) * `#5435 `_ Added information about the four sub-components of Security component (javiereguiluz) * `#5368 `_ added examples for squashing (OskarStark) @@ -37,6 +40,7 @@ New Documentation * `#5385 `_ Added a note about the need to require Composer's autoload file (javiereguiluz) * `#5386 `_ Re-write of Page Creation (weaverryan) * `#5355 `_ Added a mention to the Symfony Demo application (javiereguiluz) +* `#5331 `_ [PSR-7] Bridge documentation (dunglas) * `#5373 `_ Added mentions to some popular (and useful) Symfony bundles (javiereguiluz) Fixed Documentation @@ -51,8 +55,10 @@ Fixed Documentation * `#5431 `_ Updated the code to display flash messages (aykin, javiereguiluz) * `#5418 `_ Import Psr LogLevel (harikt) * `#5438 `_ Fixed 404 at Configuring Sessions and Save Handlers (2.3 branch) (suzuki) +* `#5412 `_ Update serializer.rst (mantulo) * `#5397 `_ Escape backslash in error message (WouterJ) * `#5379 `_ [Cookbook][Console] don't use BufferedOutput on Symfony 2.3 (xabbuh) +* `#5400 `_ Fix after install URL and new photo since AcmeDemoBundle is not part … (smatejic) * `#5350 `_ [Form][2.3] fix `validation_groups` typos (craue) * `#5358 `_ Fix typo in description (martyshka) * `#5356 `_ [Form] Fixed typo about _token field name for CSRF protection (JMLamodiere) @@ -91,6 +97,7 @@ Minor Documentation Changes * `#5288 `_ Constraints - empty strings and null values (anacicconi) * `#5284 `_ Split advanced container configuration article (WouterJ) * `#5342 `_ [Cookbook][Bundles] clarify bundle installation instructions (xabbuh) +* `#5321 `_ Use the reserved domains example.com and example.org (javiereguiluz) * `#5095 `_ Reviewed the Bundles cookbook articles (javiereguiluz) * `#4947 `_ [Components][ClassLoader] remove DebugClassLoader (xabbuh) * `#5365 `_ Finish #4967: Code style standardization on form type options (mimol91) From 9bab3bde0a3a9de929087d428ddfb242ef4fe2bb Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 1 Jul 2015 17:34:14 +0200 Subject: [PATCH 0275/2667] Updated the Symfony Versions Roadmap image --- images/release-process.jpg | Bin 69131 -> 208129 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/images/release-process.jpg b/images/release-process.jpg index 28c58b3fdadd16b86393d96a460254b8e9666c9e..df73b9b1a733a2185732e12836f07e2b24c9fed7 100644 GIT binary patch literal 208129 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�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 10000 %agW#GmkBw_z1H%#t{h`$K1ODYs~g*(C0~R30NE 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
+8$xo)06Vev~;$4TPTn%eD_9eO(dbH=Hj zQY|7)QJIDl9I;ZUYcgbSuRCwLmfrUHiuI2gsx?65E3K8o-gOlv()s>%8YJ>|e1iNm zDiXqI=@#D1UR@XWKN@&kHHzL3x|qYgg@5XP#AD(pK=XpitMosjSDtDXbmg322S~s5 zDQnGsP7uu`j1uHH<91fB{jQm+`k6B4jOiqpHq=O`ALA1;b%Btb07MLuGj}$Yc8p%) zBLDN+4V25G`6eU~fxW#kH)LW@eD4#8ue7aP2|5_Hhf$8w?!a_KwEIRy1m3o|uT(ic z9eLmwmRdY-otiv1QU73=U$V%}J7^dCOYAPzmr&X*xBgc5uhlJXXb)uEP`4yW4iJ&! zh=8V^6%xes<*0G&o#$82t<2p_zJj>8#ltmhcCz*R<%D~6^Aq(>!xMsD)#uz$H}Q_I zF-Y8a*Z9XZ>rutUL^(kRvkwAxv6q^}6sMYO;=~jP9 zrrjIZ{%b2jRj!m&zP+5J{$(#GU>CA9QRh{iRp;fB5wI)0bW(|+>PvGuCAPYwtUI=p zHD6%5t6RRo$M1n?bq+R+_$<5U_;mP-bP8brKN0^|L;;mvh>r3Om>$~#gR_5r$7YpegSCU=h8M^!fshMaX@$rhJIj=d3 zevtujn5i@%)+oeIfrY3AfzLikgA=G&JQ&zwi89AF44MDZbH}$`(#|Bekh5s!De`h% z7Q7b{6*A8+f>>vm7IK~?^qvM$(nD|Mv+9Sq9hd__Vg^3cFaRadtDR`;ZRKOK4)OW9 z<8TiwG0Moof%HNC>Ej{ofZrw{%j2PN{FJD}0SPyYuW C`O2RF literal 69131 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 X-Trt@gB{7|jJFKt@iH#{>*4 Date: Thu, 2 Jul 2015 11:41:19 +0200 Subject: [PATCH 0276/2667] Added a tip about hashing the result of nextBytes() --- components/security/secure_tools.rst | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/components/security/secure_tools.rst b/components/security/secure_tools.rst index 2ee5a98b920..c0e6965efb9 100644 --- a/components/security/secure_tools.rst +++ b/components/security/secure_tools.rst @@ -54,7 +54,14 @@ to work correctly. Just pass a file name to enable it:: .. note:: - If you're using the Symfony Framework, you can access a secure random - instance directly from the container: its name is ``security.secure_random``. + If you're using the Symfony Framework, you can get a secure random number + generator via the ``security.secure_random`` service. + +.. tip:: + + The ``nextBytes()`` method returns a binary string which may contain the + ``\0`` character. If you store this value in a database or include it as + part of the URL, make sure to hash the value returned by ``nextBytes()`` + (to do that, you can use a simple ``md5()`` PHP function). .. _`Timing attack`: http://en.wikipedia.org/wiki/Timing_attack From 89bc3260d47d52bfc6b93d3377bf290e3813fa91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 29 May 2015 14:42:04 +0200 Subject: [PATCH 0277/2667] [FrameworkBundle] Update serializer configuration reference --- cookbook/serializer.rst | 4 ++++ reference/configuration/framework.rst | 33 +++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/cookbook/serializer.rst b/cookbook/serializer.rst index 2e12c6cf641..93aba022a29 100644 --- a/cookbook/serializer.rst +++ b/cookbook/serializer.rst @@ -124,6 +124,8 @@ Here is an example on how to load the $definition->addTag('serializer.normalizer'); $container->setDefinition('get_set_method_normalizer', $definition); +.. _cookbook-serializer-using-serialization-groups-annotations: + Using Serialization Groups Annotations -------------------------------------- @@ -170,6 +172,8 @@ to your class and choose which groups to use when serializing:: 'json', array('groups' => array('group1') ); +.. _cookbook-serializer-enabling-metadata-cache: + Enabling the Metadata Cache --------------------------- diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 0ef27c453d2..b33d5d515e1 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -92,7 +92,7 @@ Configuration * `validation`_ * :ref:`enabled ` * :ref:`cache ` - * `enable_annotations`_ + * :ref:`enable_annotations ` * `translation_domain`_ * `strict_email`_ * `api`_ @@ -102,6 +102,8 @@ Configuration * `debug`_ * `serializer`_ * :ref:`enabled ` + * :ref:`cache ` + * :ref:`enable_annotations ` secret ~~~~~~ @@ -1372,6 +1374,8 @@ cache The service that is used to persist class metadata in a cache. The service has to implement the :class:`Symfony\\Component\\Validator\\Mapping\\Cache\\CacheInterface`. +.. _reference-validation-enable_annotations: + enable_annotations .................. @@ -1478,7 +1482,32 @@ enabled Whether to enable the ``serializer`` service or not in the service container. -For more details, see :doc:`/cookbook/serializer`. +.. _reference-serializer-cache: + +cache +..... + +**type**: ``string`` + +The service that is used to persist class metadata in a cache. The service +has to implement the :class:`Doctrine\\Common\\Cache\\Cache` interface. + +.. seealso:: + + For more information, see :ref:`cookbook-serializer-enabling-metadata-cache`. + +.. _reference-serializer-enable_annotations: + +enable_annotations +.................. + +**type**: ``boolean`` **default**: ``false`` + +If this option is enabled, serialization groups can be defined using annotations. + +.. seealso:: + + For more information, see :ref:`cookbook-serializer-using-serialization-groups-annotations`. Full Default Configuration -------------------------- From fda8ff526713fcf7d145e5df51a9405adae928fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20WERY?= Date: Thu, 2 Jul 2015 17:31:13 +0200 Subject: [PATCH 0278/2667] typo in components/translation/instruction.rst $translator->setFallbackLocale() should be $translator->setFallbackLocales() --- components/translation/introduction.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/translation/introduction.rst b/components/translation/introduction.rst index bcc40ced071..995d7d43591 100644 --- a/components/translation/introduction.rst +++ b/components/translation/introduction.rst @@ -170,10 +170,10 @@ example, assume you're trying to translate into the ``fr_FR`` locale: fallback locales set explicitly on the translator. For (3), the fallback locales can be set by calling -:method:`Symfony\\Component\\Translation\\Translator::setFallbackLocale`:: +:method:`Symfony\\Component\\Translation\\Translator::setFallbackLocales`:: // ... - $translator->setFallbackLocale(array('en')); + $translator->setFallbackLocales(array('en')); .. _using-message-domains: From ce518db98720f4d56d18c905d341a1caec38f771 Mon Sep 17 00:00:00 2001 From: DQNEO Date: Fri, 3 Jul 2015 00:16:50 +0900 Subject: [PATCH 0279/2667] --dev is default and causes a warning --- contributing/code/tests.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing/code/tests.rst b/contributing/code/tests.rst index 6a97ff9de6a..6e14a9d412f 100644 --- a/contributing/code/tests.rst +++ b/contributing/code/tests.rst @@ -45,7 +45,7 @@ the follow command: .. code-block:: bash - $ composer --dev update + $ composer update Running ------- From bab745d6628e2694bba9ade1fd46a13b21b67726 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 2 Jul 2015 22:49:15 +0200 Subject: [PATCH 0280/2667] Minor grammar issue --- reference/dic_tags.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index ea752f9d6c1..9788888e43b 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -337,7 +337,7 @@ wrapping their names with ``%`` characters). .. note:: - When using the ``auto_alias`` tag it's not mandatory to define the aliased + When using the ``auto_alias`` tag, it's not mandatory to define the aliased services as private. However, doing that (like in the above example) makes sense most of the times to prevent accessing those services directly instead of using the generic service alias. From 2e7757fed70f041b1e045b31b741fbe98e5bb472 Mon Sep 17 00:00:00 2001 From: Steven Date: Thu, 2 Jul 2015 21:24:52 -0500 Subject: [PATCH 0281/2667] Add cookbook article for using MongoDB to store session data --- .../configuration/mongodb_session_storage.rst | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 cookbook/configuration/mongodb_session_storage.rst diff --git a/cookbook/configuration/mongodb_session_storage.rst b/cookbook/configuration/mongodb_session_storage.rst new file mode 100644 index 00000000000..248c49a576e --- /dev/null +++ b/cookbook/configuration/mongodb_session_storage.rst @@ -0,0 +1,54 @@ +How to Use MongoDbSessionHandler to Store Sessions in a MongoDB Database +======================================================================== + +The default Symfony session storage writes the session information to files. +Some medium to large websites use a NoSQL database called MongoDB to store the +session values instead of files, because databases are easier to use and scale +in a multi-webserver environment. + +Symfony has a built-in solution for NoSQL database session storage called +:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MongoDbSessionHandler`. +MongoDB is an open-source document database that provides high performance, +high availability, and automatic scaling. This article assumes that you have +already `installed and configured a MongoDB server`_. To use it, you just +need to change/add some parameters in the main configuration file: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + framework: + session: + # ... + handler_id: session.handler.mongo + cookie_lifetime: 2592000 # optional, it is set to 30 days here + gc_maxlifetime: 2592000 # optional, it is set to 30 days here + + parameters: + # ... + mongo.session.options: + database: session_db # your MongoDB database name + collection: session # your MongoDB collection name + mongodb_host: 1.2.3.4 # your MongoDB server's IP + mongodb_username: my_username + mongodb_password: my_password + + services: + # ... + mongo_client: + class: MongoClient + # if using a username and password + arguments: [mongodb://%mongodb_username%:%mongodb_password%@%mongodb_host%:27017] + # if not using a username and password + arguments: [mongodb://%mongodb_host%:27017] + session.handler.mongo: + class: Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler + arguments: [@mongo_client, %mongo.session.options%] + +Setting Up the MongoDB Collection +--------------------------------- +Because MongoDB uses dynamic collection schemas, you do not need to do anything to initialize your +session collection. + +.. _installed and configured a MongoDB server: http://docs.mongodb.org/manual/installation/ \ No newline at end of file From 0efccb5b5fe949247121621258cdbcb49cd340d1 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Fri, 3 Jul 2015 14:19:58 +0200 Subject: [PATCH 0282/2667] Fix build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d9966ccf190..3e03572e220 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ cache: - $HOME/.pip-cache/ - _build -install: pip install --download-cache $HOME/.pip-cache --user sphinx==1.1.3 +install: pip install --download-cache $HOME/.pip-cache sphinx==1.1.3 script: sphinx-build -nW -b html -d _build/doctrees . _build/html From 4650da9ff658c852d8aea65e70dcf21e0bcc4528 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Fri, 3 Jul 2015 15:06:06 +0200 Subject: [PATCH 0283/2667] Fix caching --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3e03572e220..667ca86fa50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,10 @@ sudo: false cache: directories: - - $HOME/.pip-cache/ + - $HOME/.cache/pip - _build -install: pip install --download-cache $HOME/.pip-cache sphinx==1.1.3 +install: pip install sphinx==1.1.3 script: sphinx-build -nW -b html -d _build/doctrees . _build/html From 0eac43d58096fe5e0611d4f1ea9508f94755330a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 3 Jul 2015 17:51:33 +0200 Subject: [PATCH 0284/2667] Reverted an unneeded change --- components/console/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index eb7ab40ad16..8e8e8b188b2 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -194,7 +194,7 @@ Value Meaning It is possible to print a message in a command for only a specific verbosity level. For example:: - if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { $output->writeln(...); } From 7ca24eddbf22e45f848945548a660abcb31cde4c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 3 Jul 2015 17:52:02 +0200 Subject: [PATCH 0285/2667] Fixed RST table syntax --- components/console/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 8e8e8b188b2..444a077731b 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -184,7 +184,7 @@ Value Meaning ``OutputInterface::VERBOSITY_VERBOSE`` Increased verbosity of messages ``-v`` ``OutputInterface::VERBOSITY_VERY_VERBOSE`` Informative non essential messages ``-vv`` ``OutputInterface::VERBOSITY_DEBUG`` Debug messages ``-vvv`` -=========================================== ================================== ====================== +=========================================== ================================== ===================== .. tip:: From 8e20ba441f75bc94ab380609b4392b6616714901 Mon Sep 17 00:00:00 2001 From: Steven Date: Fri, 3 Jul 2015 12:52:12 -0500 Subject: [PATCH 0286/2667] add new cookbook article to index files --- cookbook/configuration/index.rst | 1 + cookbook/map.rst.inc | 1 + 2 files changed, 2 insertions(+) diff --git a/cookbook/configuration/index.rst b/cookbook/configuration/index.rst index 35b87ef0f2e..8bac5cf43be 100644 --- a/cookbook/configuration/index.rst +++ b/cookbook/configuration/index.rst @@ -13,3 +13,4 @@ Configuration apache_router web_server_configuration configuration_organization + mongodb_session_storage \ No newline at end of file diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 8caed945d0c..36f05e61afc 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -38,6 +38,7 @@ * :doc:`/cookbook/configuration/apache_router` * :doc:`/cookbook/configuration/web_server_configuration` * :doc:`/cookbook/configuration/configuration_organization` + * :doc:`/cookbook/configuration/mongodb_session_storage` * :doc:`/cookbook/console/index` From 52d89dc1112e6fede6c80e24a2bcd8801d7423c7 Mon Sep 17 00:00:00 2001 From: Steven Date: Fri, 3 Jul 2015 12:57:59 -0500 Subject: [PATCH 0287/2667] add xml and php configuration blocks --- .../configuration/mongodb_session_storage.rst | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/cookbook/configuration/mongodb_session_storage.rst b/cookbook/configuration/mongodb_session_storage.rst index 248c49a576e..79140e03a34 100644 --- a/cookbook/configuration/mongodb_session_storage.rst +++ b/cookbook/configuration/mongodb_session_storage.rst @@ -46,6 +46,90 @@ need to change/add some parameters in the main configuration file: class: Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler arguments: [@mongo_client, %mongo.session.options%] + .. code-block:: xml + + + + + + + + + + + + + + + session_db + + session + + + 1.2.3.4 + my_username + my_password + + + + + + mongodb://%mongodb_username%:%mongodb_password%@%mongodb_host%:27017 + + + mongodb://%mongodb_host%:27017 + + + + mongo_client + %mongo.session.options% + + + + .. code-block:: php + + use Symfony\Component\DependencyInjection\Reference; + use Symfony\Component\DependencyInjection\Definition; + + $container->loadFromExtension('framework', array( + 'session' => array( + // ... + 'handler_id' => 'session.handler.mongo', + 'cookie_lifetime' => 2592000, // optional, it is set to 30 days here + 'gc_maxlifetime' => 2592000, // optional, it is set to 30 days here + ), + )); + + $container->setParameter('mongo.session.options', array( + 'database' => 'session_db', // your MongoDB database name + 'collection' => 'session', // your MongoDB collection name + )); + $container->setParameter('mongodb_host', '1.2.3.4'); // your MongoDB server's IP + $container->setParameter('mongodb_username', 'my_username'); + $container->setParameter('mongodb_password', 'my_password'); + + $container->setDefinition('mongo_client', new Definition('MongoClient', array( + // if using a username and password + array('mongodb://%mongodb_username%:%mongodb_password%@%mongodb_host%:27017'), + // if not using a username and password + array('mongodb://%mongodb_host%:27017'), + ))); + + $container->setDefinition('session.handler.mongo', new Definition( + 'Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler', + array(new Reference('mongo_client'), '%mongo.session.options%') + )); + Setting Up the MongoDB Collection --------------------------------- Because MongoDB uses dynamic collection schemas, you do not need to do anything to initialize your From 4048e972fb8fc7d3d59b6c90abc71de271d81d7e Mon Sep 17 00:00:00 2001 From: Steven Date: Fri, 3 Jul 2015 12:59:28 -0500 Subject: [PATCH 0288/2667] fix formatting --- cookbook/configuration/mongodb_session_storage.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cookbook/configuration/mongodb_session_storage.rst b/cookbook/configuration/mongodb_session_storage.rst index 79140e03a34..32c7c69176d 100644 --- a/cookbook/configuration/mongodb_session_storage.rst +++ b/cookbook/configuration/mongodb_session_storage.rst @@ -9,7 +9,7 @@ in a multi-webserver environment. Symfony has a built-in solution for NoSQL database session storage called :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MongoDbSessionHandler`. MongoDB is an open-source document database that provides high performance, -high availability, and automatic scaling. This article assumes that you have +high availability and automatic scaling. This article assumes that you have already `installed and configured a MongoDB server`_. To use it, you just need to change/add some parameters in the main configuration file: @@ -132,6 +132,7 @@ need to change/add some parameters in the main configuration file: Setting Up the MongoDB Collection --------------------------------- + Because MongoDB uses dynamic collection schemas, you do not need to do anything to initialize your session collection. From d3ba62f27ed7c4c5de75fac2b0dc831db6521540 Mon Sep 17 00:00:00 2001 From: Steven Date: Sat, 4 Jul 2015 10:33:25 -0500 Sub 57AE ject: [PATCH 0289/2667] move parameter configuration into separate section of the article --- .../configuration/mongodb_session_storage.rst | 85 ++++++++++++------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/cookbook/configuration/mongodb_session_storage.rst b/cookbook/configuration/mongodb_session_storage.rst index 32c7c69176d..b83a43afb29 100644 --- a/cookbook/configuration/mongodb_session_storage.rst +++ b/cookbook/configuration/mongodb_session_storage.rst @@ -25,15 +25,6 @@ need to change/add some parameters in the main configuration file: cookie_lifetime: 2592000 # optional, it is set to 30 days here gc_maxlifetime: 2592000 # optional, it is set to 30 days here - parameters: - # ... - mongo.session.options: - database: session_db # your MongoDB database name - collection: session # your MongoDB collection name - mongodb_host: 1.2.3.4 # your MongoDB server's IP - mongodb_username: my_username - mongodb_password: my_password - services: # ... mongo_client: @@ -68,19 +59,6 @@ need to change/add some parameters in the main configuration file: /> - - - - session_db - - session - - - 1.2.3.4 - my_username - my_password - - @@ -110,14 +88,6 @@ need to change/add some parameters in the main configuration file: ), )); - $container->setParameter('mongo.session.options', array( - 'database' => 'session_db', // your MongoDB database name - 'collection' => 'session', // your MongoDB collection name - )); - $container->setParameter('mongodb_host', '1.2.3.4'); // your MongoDB server's IP - $container->setParameter('mongodb_username', 'my_username'); - $container->setParameter('mongodb_password', 'my_password'); - $container->setDefinition('mongo_client', new Definition('MongoClient', array( // if using a username and password array('mongodb://%mongodb_username%:%mongodb_password%@%mongodb_host%:27017'), @@ -130,6 +100,61 @@ need to change/add some parameters in the main configuration file: array(new Reference('mongo_client'), '%mongo.session.options%') )); +The parameters used above should be defined somewhere in your application, often in your main +parameters configuration: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/parameters.yml + parameters: + # ... + mongo.session.options: + database: session_db # your MongoDB database name + collection: session # your MongoDB collection name + mongodb_host: 1.2.3.4 # your MongoDB server's IP + mongodb_username: my_username + mongodb_password: my_password + + .. code-block:: xml + + + + + + + + session_db + + session + + + 1.2.3.4 + my_username + my_password + + + + .. code-block:: php + + use Symfony\Component\DependencyInjection\Reference; + use Symfony\Component\DependencyInjection\Definition; + + $container->setParameter('mongo.session.options', array( + 'database' => 'session_db', // your MongoDB database name + 'collection' => 'session', // your MongoDB collection name + )); + $container->setParameter('mongodb_host', '1.2.3.4'); // your MongoDB server's IP + $container->setParameter('mongodb_username', 'my_username'); + $container->setParameter('mongodb_password', 'my_password'); + Setting Up the MongoDB Collection --------------------------------- From dde0ee103ffe27d8f27881a625e78b7119081f71 Mon Sep 17 00:00:00 2001 From: Steven Date: Sat, 4 Jul 2015 10:44:25 -0500 Subject: [PATCH 0290/2667] add link to MongoDB cookbook article --- cookbook/session/sessions_directory.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cookbook/session/sessions_directory.rst b/cookbook/session/sessions_directory.rst index 61922ec6fcd..5ecc133ae9f 100644 --- a/cookbook/session/sessions_directory.rst +++ b/cookbook/session/sessions_directory.rst @@ -93,8 +93,9 @@ that your current sessions aren't lost when you clear Symfony's cache. Using a different session save handler is an excellent (yet more complex) method of session management available within Symfony. See :doc:`/components/http_foundation/session_configuration` for a - discussion of session save handlers. There is also an entry in the cookbook - about storing sessions in the :doc:`database `. + discussion of session save handlers. There are also entries in the cookbook + about storing sessions in a :doc:`relational database ` + or a :doc:`NoSQL database `. To change the directory in which Symfony saves session data, you only need change the framework configuration. In this example, you will change the From c7be7a30b6f25fe6d947815cf382bf0599e0bbc5 Mon Sep 17 00:00:00 2001 From: Tomas Date: Sat, 4 Jul 2015 21:17:48 +0300 Subject: [PATCH 0291/2667] Fix invalid phpunit URLs --- create_framework/unit-testing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/create_framework/unit-testing.rst b/create_framework/unit-testing.rst index 7bccdceb4bd..b34b3a76e2d 100644 --- a/create_framework/unit-testing.rst +++ b/create_framework/unit-testing.rst @@ -185,6 +185,6 @@ Symfony code. Now that we are confident (again) about the code we have written, we can safely think about the next batch of features we want to add to our framework. -.. _`PHPUnit`: http://www.phpunit.de/manual/current/en/index.html -.. _`test doubles`: http://www.phpunit.de/manual/current/en/test-doubles.html +.. _`PHPUnit`: http://phpunit.de/manual/current/en/index.html +.. _`test doubles`: http://phpunit.de/manual/current/en/test-doubles.html .. _`XDebug`: http://xdebug.org/ From 2faa2acef2369c337cb47e052d9c568c3adfaa14 Mon Sep 17 00:00:00 2001 From: Steven Date: Sun, 5 Jul 2015 10:57:18 -0500 Subject: [PATCH 0292/2667] add information about adding an index to improve garbage collection performance --- cookbook/configuration/mongodb_session_storage.rst | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cookbook/configuration/mongodb_session_storage.rst b/cookbook/configuration/mongodb_session_storage.rst index b83a43afb29..c2f912e8b9e 100644 --- a/cookbook/configuration/mongodb_session_storage.rst +++ b/cookbook/configuration/mongodb_session_storage.rst @@ -159,6 +159,13 @@ Setting Up the MongoDB Collection --------------------------------- Because MongoDB uses dynamic collection schemas, you do not need to do anything to initialize your -session collection. +session collection. However, you may want to add an index to improve garbage collection performance. +From the `MongoDB shell`_: -.. _installed and configured a MongoDB server: http://docs.mongodb.org/manual/installation/ \ No newline at end of file +.. code-block:: sql + + use session_db + db.session.ensureIndex( { "expireAt": 1 }, { expireAfterSeconds: 0 } ) + +.. _installed and configured a MongoDB server: http://docs.mongodb.org/manual/installation/ +.. _MongoDB shell: http://docs.mongodb.org/v2.2/tutorial/getting-started-with-the-mongo-shell/ \ No newline at end of file From 4238fcc883835821ca446912c61ff6e5794b4401 Mon Sep 17 00:00:00 2001 From: Steven Date: Sun, 5 Jul 2015 10:57:37 -0500 Subject: [PATCH 0293/2667] add reference to PDO and MongoDB session handlers in session index --- cookbook/session/index.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cookbook/session/index.rst b/cookbook/session/index.rst index 0420126b48e..506ce28fb57 100644 --- a/cookbook/session/index.rst +++ b/cookbook/session/index.rst @@ -8,4 +8,6 @@ Sessions locale_sticky_session sessions_directory php_bridge - avoid_session_start \ No newline at end of file + avoid_session_start + /cookbook/configuration/pdo_session_storage + /cookbook/configuration/mongodb_session_storage \ No newline at end of file From 390c05f31d2aefddc330d22f13228c96ab925a91 Mon Sep 17 00:00:00 2001 From: "german.bortoli" Date: Mon, 6 Jul 2015 14:10:18 +0100 Subject: [PATCH 0294/2667] Fixed PHP doc for how to declare the voter as a service, now they are identical to XML and YAML. --- cookbook/security/voters_data_permission.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 2376d393b53..e2c0962672e 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -169,7 +169,7 @@ and tag it with ``security.voter``: xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - @@ -182,9 +182,10 @@ and tag it with ``security.voter``: // src/AppBundle/Resources/config/services.php $container ->register( - 'security.access.post_document_voter', + 'security.access.post_voter', 'AppBundle\Security\Authorization\Voter\PostVoter' ) + ->setPublic(false) ->addTag('security.voter') ; From 5f30b073a553afb977daa5cad9ee329c8a1accdb Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 6 Jul 2015 11:00:53 -0500 Subject: [PATCH 0295/2667] add reference to mongodb session storage to cookbook index --- cookbook/map.rst.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 36f05e61afc..85b86a76225 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -188,6 +188,7 @@ * :doc:`/cookbook/session/sessions_directory` * :doc:`/cookbook/session/php_bridge` * (configuration) :doc:`/cookbook/configuration/pdo_session_storage` + * (configuration) :doc:`/cookbook/configuration/mongodb_session_storage` * :doc:`/cookbook/session/avoid_session_start` * **symfony1** From deccf85689122a458cedfff093fc83c5f7768d90 Mon Sep 17 00:00:00 2001 From: Steven Date: Tue, 7 Jul 2015 08:41:22 -0500 Subject: [PATCH 0296/2667] remove links from incorrect page --- cookbook/session/index.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cookbook/session/index.rst b/cookbook/session/index.rst index 506ce28fb57..0420126b48e 100644 --- a/cookbook/session/index.rst +++ b/cookbook/session/index.rst @@ -8,6 +8,4 @@ Sessions locale_sticky_session sessions_directory php_bridge - avoid_session_start - /cookbook/configuration/pdo_session_storage - /cookbook/configuration/mongodb_session_storage \ No newline at end of file + avoid_session_start \ No newline at end of file From 68c7364b4d01d80c51534ef1cc532be574e91399 Mon Sep 17 00:00:00 2001 From: emillosanti Date: Sun, 5 Jul 2015 20:44:05 +0800 Subject: [PATCH 0297/2667] fix #5487 --- create_framework/unit-testing.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/create_framework/unit-testing.rst b/create_framework/unit-testing.rst index b34b3a76e2d..de26e202570 100644 --- a/create_framework/unit-testing.rst +++ b/create_framework/unit-testing.rst @@ -96,6 +96,11 @@ We are now ready to write our first test:: ->method('match') ->will($this->throwException($exception)) ; + $matcher + ->expects($this->once()) + ->method('getContext') + ->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext'))) + ; $resolver = $this->getMock('Symfony\Component\HttpKernel\Controller\ControllerResolverInterface'); return new Framework($matcher, $resolver); From e5ea3f0bd7a92e19cc0bf0f75130954f4a668c88 Mon Sep 17 00:00:00 2001 From: TrueGit Date: Tue, 7 Jul 2015 15:38:04 -0400 Subject: [PATCH 0298/2667] Fix typo in url for PHPUnit test coverage report In symfony-docs/create_framework/unit-testing.rst, on topic of using PHPUnit to create a test coverage report, it appears the path to the report should be "example.com/cov/src/Simplex/Framework.php.html" instead of "example.com/cov/src_Simplex_Framework.php.html". --- create_framework/unit-testing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_framework/unit-testing.rst b/create_framework/unit-testing.rst index b34b3a76e2d..a1b38e58dae 100644 --- a/create_framework/unit-testing.rst +++ b/create_framework/unit-testing.rst @@ -173,7 +173,7 @@ coverage feature (you need to enable `XDebug`_ first): $ phpunit --coverage-html=cov/ -Open ``example.com/cov/src_Simplex_Framework.php.html`` in a browser and check +Open ``example.com/cov/src/Simplex/Framework.php.html`` in a browser and check that all the lines for the Framework class are green (it means that they have been visited when the tests were executed). From eb1ee92900aed50f15c42fc086c8e12c9b26f16c Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 7 Jul 2015 18:44:20 -0400 Subject: [PATCH 0299/2667] [#5367] Making the titles more searchable --- cookbook/map.rst.inc | 4 ++-- cookbook/security/index.rst | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index addf095e727..74eb60224c4 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -149,7 +149,7 @@ * :doc:`/cookbook/routing/redirect_trailing_slash` * :doc:`/cookbook/routing/extra_information` -* :doc:`Authentication ` +* :doc:`Security Authentication (Identifying/Logging in the User) ` * :doc:`/cookbook/security/form_login_setup` * :doc:`/cookbook/security/entity_provider` @@ -163,7 +163,7 @@ * :doc:`/cookbook/security/csrf_in_login_form` * :doc:`/cookbook/security/multiple_user_providers` -* :doc:`Authorization ` +* :doc:`Security Authorization (Denying Access) ` * :doc:`/cookbook/security/voters` * :doc:`/cookbook/security/voters_data_permission` diff --git a/cookbook/security/index.rst b/cookbook/security/index.rst index 479166d31ab..3d6a01bff6d 100644 --- a/cookbook/security/index.rst +++ b/cookbook/security/index.rst @@ -1,8 +1,8 @@ Security ======== -Authentication --------------- +Authentication (Identifying/Logging in the User) +------------------------------------------------ .. toctree:: :maxdepth: 2 @@ -19,8 +19,8 @@ Authentication csrf_in_login_form multiple_user_providers -Authorization -------------- +Authorization (Denying Access) +------------------------------ .. toctree:: :maxdepth: 2 From 57938a53672a309c9fcc02ea2da399fbeef663aa Mon Sep 17 00:00:00 2001 From: Stepan Anchugov Date: Wed, 19 Nov 2014 14:54:58 +0500 Subject: [PATCH 0300/2667] [Console] Added a cookbook entry on invoking other commands --- cookbook/console/console_command.rst | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/cookbook/console/console_command.rst b/cookbook/console/console_command.rst index 028e85f423f..64dfdd03e92 100644 --- a/cookbook/console/console_command.rst +++ b/cookbook/console/console_command.rst @@ -150,6 +150,40 @@ before translating contents:: However for other services the solution might be more complex. For more details, see :doc:`/cookbook/service_container/scopes`. +Invoking Other Commands +----------------------- + +If you need to implement a command that runs other dependent commands, you can fetch +these commands using the :class:`Symfony\\Component\\Console\\Application `'s ``find`` method. + +Also note that you'll have to pass :class:`Symfony\\Component\\Console\\Input\\InputInterface` and :class:`Symfony\\Component\\Console\\Output\\OutputInterface` +as arguments to the command's ``execute`` method. This can be easily implemented +with :class:`Symfony\\Component\\Console\\Input\\ArrayInput`:: + + protected function execute(InputInterface $input, OutputInterface $output) + { + $command = $this->getApplication()->find('some:command'); + $command->execute( + new ArrayInput(array( + 'foo' => 'foo', + '--bar' => 'foobar', + )), + $output + ); + } + +.. tip:: + + If you want to suppress the output of the executed command, pass a :class:`Symfony\\Component\\Console\\Output\\NullOutput` + as the second argument to ``$command->execute()``. + +.. caution:: + + Note that all these commands will run in the same process, and some of Symfony's + built-in commands may not work well this way. For instance, ``cache:clear`` and ``cache:warmup`` + commands change some class definitions, so running something + after them is likely to break. + Testing Commands ---------------- From b86ffb62fa48200623e2ab629d2060b0191775c7 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 22 Jun 2015 17:38:34 +0200 Subject: [PATCH 0301/2667] Removed duplication and moved a caution message --- components/console/introduction.rst | 22 ++++++++++++++----- cookbook/console/console_command.rst | 32 ++-------------------------- 2 files changed, 19 insertions(+), 35 deletions(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 78dddeea31d..3d9d34e04f2 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -449,6 +449,8 @@ method:: You can also test a whole console application by using :class:`Symfony\\Component\\Console\\Tester\\ApplicationTester`. +.. _calling-existing-command: + Calling an Existing Command --------------------------- @@ -478,16 +480,26 @@ Calling a command from another one is straightforward:: } First, you :method:`Symfony\\Component\\Console\\Application::find` the -command you want to execute by passing the command name. - -Then, you need to create a new -:class:`Symfony\\Component\\Console\\Input\\ArrayInput` with the arguments and -options you want to pass to the command. +command you want to execute by passing the command name. Then, you need to create +a new :class:`Symfony\\Component\\Console\\Input\\ArrayInput` with the arguments +and options you want to pass to the command. Eventually, calling the ``run()`` method actually executes the command and returns the returned code from the command (return value from command's ``execute()`` method). +.. tip:: + + If you want to suppress the output of the executed command, pass a :class:`Symfony\\Component\\Console\\Output\\NullOutput` + as the second argument to ``$command->execute()``. + +.. caution:: + + Note that all the commands will run in the same process, and some of Symfony's + built-in commands may not work well this way. For instance, ``cache:clear`` + and ``cache:warmup`` commands change some class definitions, so running something + after them is likely to break. + .. note:: Most of the time, calling a command from code that is not executed on the diff --git a/cookbook/console/console_command.rst b/cookbook/console/console_command.rst index 64dfdd03e92..a99e2d84b46 100644 --- a/cookbook/console/console_command.rst +++ b/cookbook/console/console_command.rst @@ -153,36 +153,8 @@ see :doc:`/cookbook/service_container/scopes`. Invoking Other Commands ----------------------- -If you need to implement a command that runs other dependent commands, you can fetch -these commands B41A using the :class:`Symfony\\Component\\Console\\Application `'s ``find`` method. - -Also note that you'll have to pass :class:`Symfony\\Component\\Console\\Input\\InputInterface` and :class:`Symfony\\Component\\Console\\Output\\OutputInterface` -as arguments to the command's ``execute`` method. This can be easily implemented -with :class:`Symfony\\Component\\Console\\Input\\ArrayInput`:: - - protected function execute(InputInterface $input, OutputInterface $output) - { - $command = $this->getApplication()->find('some:command'); - $command->execute( - new ArrayInput(array( - 'foo' => 'foo', - '--bar' => 'foobar', - )), - $output - ); - } - -.. tip:: - - If you want to suppress the output of the executed command, pass a :class:`Symfony\\Component\\Console\\Output\\NullOutput` - as the second argument to ``$command->execute()``. - -.. caution:: - - Note that all these commands will run in the same process, and some of Symfony's - built-in commands may not work well this way. For instance, ``cache:clear`` and ``cache:warmup`` - commands change some class definitions, so running something - after them is likely to break. +See :ref:`calling-existing-command` if you need to implement a command that runs +other dependent commands. Testing Commands ---------------- From 2c491bee6a0a7ba09fcdbf3886083c2ffcdaacd8 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 23 Jun 2015 10:10:08 +0200 Subject: [PATCH 0302/2667] Fixed typos --- components/console/introduction.rst | 13 +++++++------ cookbook/console/console_command.rst | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 3d9d34e04f2..1fc64b18491 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -490,15 +490,16 @@ returns the returned code from the command (return value from command's .. tip:: - If you want to suppress the output of the executed command, pass a :class:`Symfony\\Component\\Console\\Output\\NullOutput` - as the second argument to ``$command->execute()``. + If you want to suppress the output of the executed command, pass a + :class:`Symfony\\Component\\Console\\Output\\NullOutput` as the second + argument to ``$command->execute()``. .. caution:: - Note that all the commands will run in the same process, and some of Symfony's - built-in commands may not work well this way. For instance, ``cache:clear`` - and ``cache:warmup`` commands change some class definitions, so running something - after them is likely to break. + Note that all the commands will run in the same process and some of Symfony's + built-in commands may not work well this way. For instance, the ``cache:clear`` + and ``cache:warmup`` commands change some class definitions, so running + something after them is likely to break. .. note:: diff --git a/cookbook/console/console_command.rst b/cookbook/console/console_command.rst index a99e2d84b46..84101b50ae3 100644 --- a/cookbook/console/console_command.rst +++ b/cookbook/console/console_command.rst @@ -150,7 +150,7 @@ before translating contents:: However for other services the solution might be more complex. For more details, see :doc:`/cookbook/service_container/scopes`. -Invoking Other Commands +Invoking other Commands ----------------------- See :ref:`calling-existing-command` if you need to implement a command that runs From 3395e025f127a320d8c32dc57db674c4860dd2eb Mon Sep 17 00:00:00 2001 From: Sarah KHALIL Date: Fri, 12 Dec 2014 00:19:14 +0100 Subject: [PATCH 0303/2667] [Validator] Updated documentation of URL validator Updated the documentation regarding the Pull Request on Symfony : https://github.com/symfony/symfony/pull/12956 --- reference/constraints/Url.rst | 36 ++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/reference/constraints/Url.rst b/reference/constraints/Url.rst index 26715d55c02..b207fe5dc28 100644 --- a/reference/constraints/Url.rst +++ b/reference/constraints/Url.rst @@ -19,6 +19,16 @@ Basic Usage .. configuration-block:: + .. code-block:: yaml + + # src/Acme/BlogBundle/Resources/config/validation.yml + Acme\BlogBundle\Entity\Author: + properties: + bioUrl: + - Url: ~ + message: The url "{{ value }}" is not a valid url. + protocols: [http, https] + .. code-block:: php-annotations // src/Acme/BlogBundle/Entity/Author.php @@ -29,19 +39,14 @@ Basic Usage class Author { /** - * @Assert\Url() + * @Assert\Url( + * message = "The url '{{ value }}' is not a valid url", + * protocols = {"http", "https"} + * ) */ protected $bioUrl; } - .. code-block:: yaml - - # src/Acme/BlogBundle/Resources/config/validation.yml - Acme\BlogBundle\Entity\Author: - properties: - bioUrl: - - Url: ~ - .. code-block:: xml @@ -52,7 +57,13 @@ Basic Usage - + + + + @@ -69,7 +80,10 @@ Basic Usage { public static function loadValidatorMetadata(ClassMetadata $metadata) { - $metadata->addPropertyConstraint('bioUrl', new Assert\Url()); + $metadata->addPropertyConstraint('bioUrl', new Assert\Url(array( + 'message' => 'The url "{{ value }}" is not a valid url.', + 'protocols' => array('http', 'https'), + ))); } } From 07628c962433ab7ac4aedfce1a994ff4a794ec07 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 23 Jun 2015 13:26:47 +0200 Subject: [PATCH 0304/2667] Simplified the first example and added more examples --- reference/constraints/Url.rst | 154 +++++++++++++++++++++++++++++----- 1 file changed, 135 insertions(+), 19 deletions(-) diff --git a/reference/constraints/Url.rst b/reference/constraints/Url.rst index b207fe5dc28..0fc7bf8c6e0 100644 --- a/reference/constraints/Url.rst +++ b/reference/constraints/Url.rst @@ -17,6 +17,72 @@ Validates that a value is a valid URL string. Basic Usage ----------- +.. configuration-block:: + + .. code-block:: yaml + + # src/Acme/BlogBundle/Resources/config/validation.yml + Acme\BlogBundle\Entity\Author: + properties: + bioUrl: + - Url: ~ + + .. code-block:: php-annotations + + // src/Acme/BlogBundle/Entity/Author.php + namespace Acme\BlogBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Author + { + /** + * @Assert\Url() + */ + protected $bioUrl; + } + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // src/Acme/BlogBundle/Entity/Author.php + namespace Acme\BlogBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Author + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('bioUrl', new Assert\Url()); + } + } + +Options +------- + +message +~~~~~~~ + +**type**: ``string`` **default**: ``This value is not a valid URL.`` + +This message is shown if the URL is invalid. + .. configuration-block:: .. code-block:: yaml @@ -27,7 +93,6 @@ Basic Usage bioUrl: - Url: ~ message: The url "{{ value }}" is not a valid url. - protocols: [http, https] .. code-block:: php-annotations @@ -41,7 +106,6 @@ Basic Usage /** * @Assert\Url( * message = "The url '{{ value }}' is not a valid url", - * protocols = {"http", "https"} * ) */ protected $bioUrl; @@ -59,10 +123,6 @@ Basic Usage - @@ -82,26 +142,82 @@ Basic Usage { $metadata->addPropertyConstraint('bioUrl', new Assert\Url(array( 'message' => 'The url "{{ value }}" is not a valid url.', - 'protocols' => array('http', 'https'), ))); } } -Options -------- +protocols +~~~~~~~~~ -message -~~~~~~~ +**type**: ``array`` **default**: ``array('http', 'https')`` -**type**: ``string`` **default**: ``This value is not a valid URL.`` +The protocols considered to be valid for the URL. For example, if you also consider +the ``ftp://`` type URLs to be valid, redefine the ``protocols`` array, listing +``http``, ``https``, and also ``ftp``. -This message is shown if the URL is invalid. +.. configuration-block:: -protocols -~~~~~~~~~ + .. code-block:: yaml -**type**: ``array`` **default**: ``array('http', 'https')`` + # src/Acme/BlogBundle/Resources/config/validation.yml + Acme\BlogBundle\Entity\Author: + properties: + bioUrl: + - Url: ~ + protocols: [http, https, ftp] + + .. code-block:: php-annotations + + // src/Acme/BlogBundle/Entity/Author.php + namespace Acme\BlogBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Author + { + /** + * @Assert\Url( + * protocols = {"http", "https", "ftp"} + * ) + */ + protected $bioUrl; + } + + .. code-block:: xml -The protocols that will be considered to be valid. For example, if you also -needed ``ftp://`` type URLs to be valid, you'd redefine the ``protocols`` -array, listing ``http``, ``https`` and also ``ftp``. + + + + + + + + + + + + + + .. code-block:: php + + // src/Acme/BlogBundle/Entity/Author.php + namespace Acme\BlogBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Author + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('bioUrl', new Assert\Url(array( + 'protocols' => array('http', 'https', 'ftp'), + ))); + } + } From fa7ca6d670f95ebbdb838b900c68d3fb22ad676b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 24 Jun 2015 16:37:38 +0200 Subject: [PATCH 0305/2667] Fixed the order of the examples --- reference/constraints/Url.rst | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/reference/constraints/Url.rst b/reference/constraints/Url.rst index 0fc7bf8c6e0..9871fa8aba1 100644 --- a/reference/constraints/Url.rst +++ b/reference/constraints/Url.rst @@ -85,15 +85,6 @@ This message is shown if the URL is invalid. .. configuration-block:: - .. code-block:: yaml - - # src/Acme/BlogBundle/Resources/config/validation.yml - Acme\BlogBundle\Entity\Author: - properties: - bioUrl: - - Url: ~ - message: The url "{{ value }}" is not a valid url. - .. code-block:: php-annotations // src/Acme/BlogBundle/Entity/Author.php @@ -111,6 +102,15 @@ This message is shown if the URL is invalid. protected $bioUrl; } + .. code-block:: yaml + + # src/Acme/BlogBundle/Resources/config/validation.yml + Acme\BlogBundle\Entity\Author: + properties: + bioUrl: + - Url: ~ + message: The url "{{ value }}" is not a valid url. + .. code-block:: xml @@ -157,15 +157,6 @@ the ``ftp://`` type URLs to be valid, redefine the ``protocols`` array, listing .. configuration-block:: - .. code-block:: yaml - - # src/Acme/BlogBundle/Resources/config/validation.yml - Acme\BlogBundle\Entity\Author: - properties: - bioUrl: - - Url: ~ - protocols: [http, https, ftp] - .. code-block:: php-annotations // src/Acme/BlogBundle/Entity/Author.php @@ -183,6 +174,15 @@ the ``ftp://`` type URLs to be valid, redefine the ``protocols`` array, listing protected $bioUrl; } + .. code-block:: yaml + + # src/Acme/BlogBundle/Resources/config/validation.yml + Acme\BlogBundle\Entity\Author: + properties: + bioUrl: + - Url: ~ + protocols: [http, https, ftp] + .. code-block:: xml From f13625b22a89a511a54cf6fda6ba4ac39b624932 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 24 Jun 2015 16:38:33 +0200 Subject: [PATCH 0306/2667] Added the "payload" option back (was removed by mistake) --- reference/constraints/Url.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/reference/constraints/Url.rst b/reference/constraints/Url.rst index 9871fa8aba1..245192afc76 100644 --- a/reference/constraints/Url.rst +++ b/reference/constraints/Url.rst @@ -8,6 +8,11 @@ Validates that a value is a valid URL string. +----------------+---------------------------------------------------------------------+ | Options | - `message`_ | | | - `protocols`_ | +<<<<<<< HEAD +======= +| | - `payload`_ | +| | - `checkDNS`_ | +>>>>>>> Added the "payload" option back (was removed by mistake) +----------------+---------------------------------------------------------------------+ | Class | :class:`Symfony\\Component\\Validator\\Constraints\\Url` | +----------------+---------------------------------------------------------------------+ From 5d7b2a1346881bddda2a08d40a56ee1ee8ba3bc5 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jun 2015 09:30:25 +0200 Subject: [PATCH 0307/2667] Fixed some errors and made some simplifications --- reference/constraints/Url.rst | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/reference/constraints/Url.rst b/reference/constraints/Url.rst index 245192afc76..e168c13dd31 100644 --- a/reference/constraints/Url.rst +++ b/reference/constraints/Url.rst @@ -8,11 +8,6 @@ Validates that a value is a valid URL string. +----------------+---------------------------------------------------------------------+ | Options | - `message`_ | | | - `protocols`_ | -<<<<<<< HEAD -======= -| | - `payload`_ | -| | - `checkDNS`_ | ->>>>>>> Added the "payload" option back (was removed by mistake) +----------------+---------------------------------------------------------------------+ | Class | :class:`Symfony\\Component\\Validator\\Constraints\\Url` | +----------------+---------------------------------------------------------------------+ @@ -113,7 +108,7 @@ This message is shown if the URL is invalid. Acme\BlogBundle\Entity\Author: properties: bioUrl: - - Url: ~ + - Url: message: The url "{{ value }}" is not a valid url. .. code-block:: xml @@ -185,8 +180,7 @@ the ``ftp://`` type URLs to be valid, redefine the ``protocols`` array, listing Acme\BlogBundle\Entity\Author: properties: bioUrl: - - Url: ~ - protocols: [http, https, ftp] + - Url: { protocols: [http, https, ftp] } .. code-block:: xml From 1135910c83a115582330de9264a5789c46b263ca Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jun 2015 09:46:30 +0200 Subject: [PATCH 0308/2667] Reordered the configuration blocks of the first example --- reference/constraints/Url.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/reference/constraints/Url.rst b/reference/constraints/Url.rst index e168c13dd31..2a4cace9c86 100644 --- a/reference/constraints/Url.rst +++ b/reference/constraints/Url.rst @@ -19,14 +19,6 @@ Basic Usage .. configuration-block:: - .. code-block:: yaml - - # src/Acme/BlogBundle/Resources/config/validation.yml - Acme\BlogBundle\Entity\Author: - properties: - bioUrl: - - Url: ~ - .. code-block:: php-annotations // src/Acme/BlogBundle/Entity/Author.php @@ -42,6 +34,14 @@ Basic Usage protected $bioUrl; } + .. code-block:: yaml + + # src/Acme/BlogBundle/Resources/config/validation.yml + Acme\BlogBundle\Entity\Author: + properties: + bioUrl: + - Url: ~ + .. code-block:: xml From cc4c4489f8801993dde72148d9c37511ce76939d Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 7 Jul 2015 19:04:02 -0400 Subject: [PATCH 0309/2667] [#5426] Porting the checkDNS option to 2.7 only --- reference/constraints/Url.rst | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/reference/constraints/Url.rst b/reference/constraints/Url.rst index 09f7793b4c4..5228c91f42b 100644 --- a/reference/constraints/Url.rst +++ b/reference/constraints/Url.rst @@ -9,6 +9,7 @@ Validates that a value is a valid URL string. | Options | - `message`_ | | | - `protocols`_ | | | - `payload`_ | +| | - `checkDNS`_ | +----------------+---------------------------------------------------------------------+ | Class | :class:`Symfony\\Component\\Validator\\Constraints\\Url` | +----------------+---------------------------------------------------------------------+ @@ -223,3 +224,80 @@ the ``ftp://`` type URLs to be valid, redefine the ``protocols`` array, listing } .. include:: /reference/constraints/_payload-option.rst.inc + +checkDNS +~~~~~~~~ + +.. versionadded:: 2.7 + The ``checkDNS`` option was introduced in Symfony 2.7. + +**type**: ``boolean`` **default**: ``false`` + +By default, this constraint just validates the syntax of the given URL. If you +also need to check whether the associated host exists, set the ``checkDNS`` +option to ``true``: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/BlogBundle/Entity/Author.php + namespace Acme\BlogBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Author + { + /** + * @Assert\Url( + * checkDNS = true + * ) + */ + protected $bioUrl; + } + + .. code-block:: yaml + + # src/Acme/BlogBundle/Resources/config/validation.yml + Acme\BlogBundle\Entity\Author: + properties: + bioUrl: + - Url: { checkDNS: true } + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: php + + // src/Acme/BlogBundle/Entity/Author.php + namespace Acme\BlogBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Author + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('bioUrl', new Assert\Url(array( + 'checkDNS' => true, + ))); + } + } + +This option uses the :phpfunction:`checkdnsrr` PHP function to check the validity +of the ``ANY`` DNS record corresponding to the host associated with the given URL. From cdb9067930741411e600070c56c6a56c87351f0d Mon Sep 17 00:00:00 2001 From: Karel Souffriau Date: Tue, 7 Jul 2015 14:41:47 +0200 Subject: [PATCH 0310/2667] The "property" option of DoctrineType was deprecated. --- reference/forms/types/entity.rst | 54 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/reference/forms/types/entity.rst b/reference/forms/types/entity.rst index d5a7118e1f3..007f439efab 100644 --- a/reference/forms/types/entity.rst +++ b/reference/forms/types/entity.rst @@ -12,11 +12,11 @@ objects from the database. +-------------+------------------------------------------------------------------+ | Rendered as | can be various tags (see :ref:`forms-reference-choice-tags`) | +-------------+------------------------------------------------------------------+ -| Options | - `class`_ | +| Options | - `choice_label`_ | +| | - `class`_ | | | - `data_class`_ | | | - `em`_ | | | - `group_by`_ | -| | - `property`_ | | | - `query_builder`_ | +-------------+------------------------------------------------------------------+ | Overridden | - `choice_list`_ | @@ -55,13 +55,13 @@ be listed inside the choice field:: $builder->add('users', 'entity', array( 'class' => 'AcmeHelloBundle:User', - 'property' => 'username', + 'choice_label' => 'username', )); In this case, all ``User`` objects will be loaded from the database and rendered as either a ``select`` tag, a set or radio buttons or a series of checkboxes (this depends on the ``multiple`` and ``expanded`` values). -If the entity object does not have a ``__toString()`` method the ``property`` +If the entity object does not have a ``__toString()`` method the ``choice_label`` option is needed. Using a Custom Query for the Entities @@ -103,6 +103,29 @@ then you can supply the ``choices`` option directly:: Field Options ------------- +choice_label +~~~~~~~~~~~~ + +**type**: ``string`` + +This is the property that should be used for displaying the entities +as text in the HTML element. If left blank, the entity object will be +cast into a string and so must have a ``__toString()`` method. + +.. note:: + + The ``choice_label`` option is the property path used to display the option. + So you can use anything supported by the + :doc:`PropertyAccessor component ` + + For example, if the translations property is actually an associative + array of objects, each with a name property, then you could do this:: + + $builder->add('gender', 'entity', array( + 'class' => 'MyBundle:Gender', + 'choice_label' => 'translations[en].name', + )); + class ~~~~~ @@ -133,29 +156,6 @@ and does so by adding ``optgroup`` elements around options. Choices that do not return a value for this property path are rendered directly under the select tag, without a surrounding optgroup. -property -~~~~~~~~ - -**type**: ``string`` - -This is the property that should be used for displaying the entities -as text in the HTML element. If left blank, the entity object will be -cast into a string and so must have a ``__toString()`` method. - -.. note:: - - The ``property`` option is the property path used to display the option. - So you can use anything supported by the - :doc:`PropertyAccessor component ` - - For example, if the translations property is actually an associative - array of objects, each with a name property, then you could do this:: - - $builder->add('gender', 'entity', array( - 'class' => 'MyBundle:Gender', - 'property' => 'translations[en].name', - )); - query_builder ~~~~~~~~~~~~~ From b48feda2fa3c4a2d8f1f274c61ba605a7fa4a4ce Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 27 Jun 2015 21:31:50 -0400 Subject: [PATCH 0311/2667] Completely re-reading the data transformers chapter: 1) Show an easy example using the CallbackTransformer in the beginning 2) Remove complexity related to any factories that were there before 3) Moved theoretical section to the bottom --- book/forms.rst | 2 + cookbook/form/data_transformers.rst | 519 ++++++++++++++-------------- 2 files changed, 266 insertions(+), 255 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index 38186bba172..71b6695bb75 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -1141,6 +1141,8 @@ the choice is ultimately up to you. $form->get('dueDate')->setData(new \DateTime()); +.. _form-as-services: + Defining your Forms as Services ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index 850db1a5adb..5ae723232c5 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -4,29 +4,143 @@ How to Use Data Transformers ============================ -You'll often find the need to transform the data the user entered in a form into -something else for use in your program. You could easily do this manually in your -controller, but what if you want to use this specific form in different places? - -Say you have a one-to-one relation of Task to Issue, e.g. a Task optionally has an -issue linked to it. Adding a listbox with all possible issues can eventually lead to -a really long listbox in which it is impossible to find something. You might -want to add a textbox instead, where the user can simply enter the issue number. - -You could try to do this in your controller, but it's not the best solution. -It would be better if this issue were automatically converted to an Issue object. -This is where Data Transformers come into play. +Data transformers are used to translate the data for a field into a format that can +be displayed in a form (and back on submit). They're already used internally for +many field types. For example, the :doc:`date field type ` +can be rendered as a ``yyyy-MM-dd``-formatted input textbox. Internally, a data transformer +converts the starting ``DateTime`` value of the field into the ``yyyy-MM-dd`` string +to render the form, and then back into a ``DateTime`` object on submit. .. caution:: When a form field has the ``inherit_data`` option set, Data Transformers won't be applied to that field. +Simple Example: Sanitizing HTML on User Input +--------------------------------------------- + +Suppose you have a Task form with a description ``textarea`` type:: + + // src/AppBundle/Form/TaskType.php + // ... + + class TaskType extends AbstractType + { + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->add('description', 'textarea'); + } + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'data_class' => 'AppBundle\Entity\Task' + )); + } + + // ... + } + +But, there are two complications: + +#. Your users are allowed to use *some* HTML tags, but not others: you need a way + to call :phpfunction:`striptags` after the form is submitted. + +#. To be friendly, you want to convert ``
`` tags into line breaks (``\n``) before + rendering the field so the text is easier to edit. + +This is a *perfect* time to attach a custom data transformer to the ``description`` +field. The easiest way to do this is with the :class:`Symfony\\Component\\Form\\CallbackTransformer` +class:: + + // src/AppBundle/Form/TaskType.php + + use Symfony\Component\Form\CallbackTransformer; + // ... + + class TaskType extends AbstractType + { + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->add('description', 'textarea'); + + $builder->get('description') + ->addModelTransformer(new CallbackTransformer( + // transform
to \n so the textarea reads easier + function ($originalDescription) { + return preg_replace('##i', "\n", $originalDescription); + }, + function ($submittedDescription) { + // remove most HTML tags (but not br,p) + $cleaned = strip_tags($submittedDescription, '

'); + + // transform any \n to real
+ return str_replace("\n", '
', $cleaned); + } + )); + } + + // ... + } + +The ``CallbackTransformer`` takes to callback functions as arguments. The first transforms +the original value into a format that'll be used to render the field. The second +does the reverse: it transforms the submitted value back into the format you'll use +in your code. + +.. tip:: + + The ``addModelTransformer()`` method accepts *any* object that implements + :class:`Symfony\\Component\\Form\\DataTransformerInterface` - so you can create + your own classes, instead of putting all the logic in the form (see the next section). + +Harder Examle: Transforming an Issue Number into an Issue Entity +---------------------------------------------------------------- + +Say you have a many-to-one relation from the Task entity to an Issue entity (i.e. each +Task has an optional foreign key to its related Issue). Adding a listbox with all +possible issues could eventually get *really* long and take a long time to load. +Instead, you decide you want to add a textbox, where the user can simply enter the +issue number. + +Start by setting up the text field like normal:: + + // src/AppBundle/Form/TaskType.php + // ... + + class TaskType extends AbstractType + { + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->add('description', 'textarea') + ->add('issue', 'text'); + } + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'data_class' => 'AppBundle\Entity\Task' + )); + } + + // ... + } + +Good start! But if you stopped here and submitted the form, the Task's ``issue`` +property would be a string (e.g. "55"). How can you transform this into an ``Issue`` +entity on submit? + Creating the Transformer ------------------------ -First, create an ``IssueToNumberTransformer`` class - this class will be responsible -for converting to and from the issue number and the ``Issue`` object:: +You could use the ``CallbackTransformer`` like earlier. But since this is a bit more +complex, creating a new transformer class will keep the ``TaskType`` form class simpler. + +Create an ``IssueToNumberTransformer`` class: it will be responsible for converting +to and from the issue number and the ``Issue`` object:: // src/AppBundle/Form/DataTransformer/IssueToNumberTransformer.php namespace AppBundle\Form\DataTransformer; @@ -38,17 +152,11 @@ for converting to and from the issue number and the ``Issue`` object:: class IssueToNumberTransformer implements DataTransformerInterface { - /** - * @var ObjectManager - */ - private $om; + private $em; - /** - * @param ObjectManager $om - */ - public function __construct(ObjectManager $om) + public function __construct(ObjectManager $em) { - $this->om = $om; + $this->em = $em; } /** @@ -63,31 +171,36 @@ for converting to and from the issue number and the ``Issue`` object:: return ''; } - return $issue->getNumber(); + return $issue->getId(); } /** * Transforms a string (number) to an object (issue). * - * @param string $number + * @param string $issueNumber * @return Issue|null * @throws TransformationFailedException if object (issue) is not found. */ - public function reverseTransform($number) + public function reverseTransform($issueNumber) { - if (!$number) { + // no issue number? It's optional, so that's ok + if (!$issueNumber) { return null; } - $issue = $this->om + $issue = $this->em ->getRepository('AppBundle:Issue') - ->findOneBy(array('number' => $number)) + // query for the issue with this id + ->find($issueNumber) ; if (null === $issue) { + // causes a validation error + // this message is not shown to the user + // see the invalid_message option throw new TransformationFailedException(sprintf( 'An issue with number "%s" does not exist!', - $number + $issueNumber )); } @@ -95,10 +208,15 @@ for converting to and from the issue number and the ``Issue`` object:: } } -.. tip:: +Just like in the first example, a transformer has two directions. The ``transform()`` +method is responsible for converting the data used in your code to a format that +can be rendered in your form (e.g. an ``Issue`` object to its ``id``, a string). +The ``reverseTransform()`` method does the reverse: it converts the submitted value +back into the format you want (e.g. convert the ``id`` back to the ``Issue`` object). - If you want a new issue to be created when an unknown number is entered, you - can instantiate it rather than throwing the ``TransformationFailedException``. +To cause a validation error, throw a :class:`Symfony\\Component\\Form\\Exception\\TransformationFailedException`. +But the message you pass to this exception won't be shown to the user. You'll set +that message with the ``invalid_message`` option (see below). .. note:: @@ -109,140 +227,56 @@ for converting to and from the issue number and the ``Issue`` object:: Using the Transformer --------------------- -As seen above our transformer requires an instance of an object manager. While for most -use-cases it is sufficient to use the default entity manager, you will sometimes need -to explicitly choose the one to use. To achieve this, you can use a factory:: - - // src/AppBundle/Form/DataTransformer/IssueToNumberTransformerFactory.php - namespace AppBundle\Form\DataTransformer; +Next, you need to instantiate the ``IssueToNumberTransformer`` class from inside +``TaskType`` and add it to the ``issue`` field. But to do that, you'll need an instance +of the entity manager (because ``IssueToNumberTransformer`` needs this). - use Doctrine\Common\Persistence\ManagerRegistry; - - class IssueToNumberTransformerFactory - { - /** - * @var ManagerRegistry - */ - private $registry; - - public function __construct(ManagerRegistry $registry) - { - $this->registry = $registry; - } - - public function create($om) - { - return new IssueToNumberTransformer($this->registry->getManager($om)); - } - } - -.. configuration-block:: - - .. code-block:: yaml - - services: - app.issue_transformer_factory: - class: AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory - arguments: ["@doctrine"] - public: false - - app.type.task: - class: AppBundle\Form\TaskType - arguments: ["@app.issue_transformer_factory"] - tags: - - { name: form.type, alias: app_task } - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - use Symfony\Component\DependencyInjection\Definition; - use Symfony\Component\DependencyInjection\Reference; - // ... - - $container - ->setDefinition('app.issue_transformer_factory', new Definition( - 'AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory' - ), array( - new Reference('doctrine'), - )) - ->setPublic(false) - ; - - $container - ->setDefinition('app.type.task', new Definition( - 'AppBundle\Form\TaskType' - ), array( - new Reference('app.issue_transformer_factory'), - )) - ->addTag('form.type', array('alias' => 'app_task')) - ; - -Now that you have the capability to build the transformer with the desired object manager, you -just need to create it from your issue field in some form. - -You can also use transformers without creating a new custom form type -by calling ``addModelTransformer`` (or ``addViewTransformer`` - see -`Model and View Transformers`_) on any field builder:: +No problem! Just add a ``__construct()`` function to ``TaskType`` and force this +to be passed in. Then, you can easily create and add the transformer:: // src/AppBundle/Form/TaskType.php - namespace AppBundle\Form; - - use AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory; - use Symfony\Component\Form\FormBuilderInterface; - use Symfony\Component\OptionsResolver\OptionsResolverInterface; + use AppBundle\Form\DataTransformer\IssueToNumberTransformer; + use Doctrine\Common\Persistence\ObjectManager; class TaskType extends AbstractType { - /** - * @var IssueToNumberTransformerFactory - */ - private $factory; + private $em; - public function __construct(IssueToNumberTransformerFactory $factory) + public function __construct(ObjectManager $em) { - $this->factory = $factory; + $this->em = $em; } public function buildForm(FormBuilderInterface $builder, array $options) { - $transformer = $this->factory->create($options['om']); + $builder + ->add('description', 'textarea') + ->add('issue', 'text', array( + // validation message if the data transformer fails + 'invalid_message' => 'That is not a valid issue number' + )); - $builder->add( - $builder->create('issue', 'text') - ->addModelTransformer($transformer) - ); - } + // ... - public function setDefaultOptions(OptionsResolverInterface $resolver) - { - $resolver - ->setDefaults(array( - 'data_class' => 'AppBundle\Entity\Task', - )) - ->setRequired(array('om')) - ; + $builder->get('issue') + ->addModelTransformer(new IssueToNumberTransformer($this->em)); } + + // ... } -This example requires that you pass in the entity manager as an option -when creating your form. Later, you'll learn how you could create a custom -``issue`` field type to avoid needing to do this in your controller:: +Now, when you create your ``TaskType``, you'll need to pass in the entity manager:: - $taskForm = $this->createForm('app_task', $task, array( - 'om' => 'default', - )); + // e.g. in a controller somewhere + $em = $this->getDoctrine()->getManager(); + $form = $this->createForm(new TaskType($em), $task); + + // ... + +.. note:: + + To make this step easier (especially if ``TaskType`` is embedded into other + form type classes), you might choose to :ref:`register your form type as a service `. Cool, you're done! Your user will be able to enter an issue number into the text field and it will be transformed back into an Issue object. This means @@ -254,103 +288,47 @@ its error message can be controlled with the ``invalid_message`` field option. .. caution:: - Notice that adding a transformer requires using a slightly more complicated - syntax when add F438 ing the field. The following is **wrong**, as the transformer - would be applied to the entire form, instead of just this field:: + Be careful when adding your transformers. For example, the following is **wrong**, + as the transformer would be applied to the entire form, instead of just this + field:: // THIS IS WRONG - TRANSFORMER WILL BE APPLIED TO THE ENTIRE FORM // see above example for correct code $builder->add('issue', 'text') ->addModelTransformer($transformer); -Model and View Transformers -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In the above example, the transformer was used as a "model" transformer. -In fact, there are two different types of transformers and three different -types of underlying data. - -.. image:: /images/cookbook/form/DataTransformersTypes.png - :align: center +Creating a Reusable issue_selector Field +---------------------------------------- -In any form, the three different types of data are: - -1) **Model data** - This is the data in the format used in your application - (e.g. an ``Issue`` object). If you call ``Form::getData`` or ``Form::setData``, - you're dealing with the "model" data. - -2) **Norm Data** - This is a normalized version of your data, and is commonly - the same as your "model" data (though not in our example). It's not commonly - used directly. - -3) **View Data** - This is the format that's used to fill in the form fields - themselves. It's also the format in which the user will submit the data. When - you call ``Form::submit($data)``, the ``$data`` is in the "view" data format. - -The two different types of transformers help convert to and from each of these -types of data: - -**Model transformers**: - - ``transform``: "model data" => "norm data" - - ``reverseTransform``: "norm data" => "model data" - -**View transformers**: - - ``transform``: "norm data" => "view data" - - ``reverseTransform``: "view data" => "norm data" +In the above example, you applied the transformer to a normal ``text`` field. But +if you do this transformation a lot, it might be better to +:doc:`create a custom field type `. +that does this automatically. -Which transformer you need depends on your situation. - -To use the view transformer, call ``addViewTransformer``. - -So why Use the Model Transformer? ---------------------------------- - -In this example, the field is a ``text`` field, and a text field is always -expected to be a simple, scalar format in the "norm" and "view" formats. For -this reason, the most appropriate transformer was the "model" transformer -(which converts to/from the *norm* format - string issue number - to the *model* -format - Issue object). - -The difference between the transformers is subtle and you should always think -about what the "norm" data for a field should really be. For example, the -"norm" data for a ``text`` field is a string, but is a ``DateTime`` object -for a ``date`` field. - -Using Transformers in a custom Field Type ------------------------------------------ - -In the above example, you applied the transformer to a normal ``text`` field. -This was easy, but has two downsides: - -1) You need to always remember to apply the transformer whenever you're adding -a field for issue numbers. - -2) You need to worry about passing in the ``em`` option whenever you're creating -a form that uses the transformer. - -Because of these, you may choose to :doc:`create a custom field type `. First, create the custom field type class:: // src/AppBundle/Form/IssueSelectorType.php + namespace AppBundle\Form; - use AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory; + use AppBundle\Form\DataTransformer\IssueToNumberTransformer; + use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; class IssueSelectorType extends AbstractType { - private $factory; - - public function __construct(IssueToNumberTransformerFactory $factory) + private $em; + + public function __construct(ObjectManager $em) { - $this->factory = $factory; + $this->em = $em; } public function buildForm(FormBuilderInterface $builder, array $options) { - $transformer = $this->factory->create($options['om']); + $transformer = new IssueToNumberTransformer($this->em); $builder->addModelTransformer($transformer); } @@ -358,7 +336,6 @@ First, create the custom field type class:: { $resolver->setDefaults(array( 'invalid_message' => 'The selected issue does not exist', - 'om' => 'default' )); } @@ -373,6 +350,9 @@ First, create the custom field type class:: } } +Great! This will act and render like a text field (``getParent()``), but will automatically +have the data transformer *and* a nice default value for the ``invalid_message`` option. + Next, register your type as a service and tag it with ``form.type`` so that it's recognized as a custom field type: @@ -381,26 +361,18 @@ it's recognized as a custom field type: .. code-block:: yaml services: - app.issue_transformer_factory: - class: AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory - arguments: ["@doctrine"] - public: false app.type.issue_selector: class: AppBundle\Form\IssueSelectorType - arguments: ["@app.issue_transformer_factory"] + arguments: ["@doctrine.orm.default_entity_manager"] tags: - { name: form.type, alias: issue_selector } - .. code-block:: xml - - - + .. code-block:: xml - + @@ -409,21 +381,12 @@ it's recognized as a custom field type: use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; // ... - - $container - ->setDefinition('app.issue_transformer_factory', new Definition( - 'AppBundle\Form\DataTransformer\IssueToNumberTransformerFactory' - ), array( - new Reference('doctrine'), - )) - ->setPublic(false) - ; - + $container ->setDefinition('app.type.issue_selector', new Definition( 'AppBundle\Form\IssueSelectorType' ), array( - new Reference('app.issue_transformer_factory'), + new Reference('doctrine.orm.default_entity_manager'), )) ->addTag('form.type', array( 'alias' => 'issue_selector', @@ -434,23 +397,69 @@ Now, whenever you need to use your special ``issue_selector`` field type, it's quite easy:: // src/AppBundle/Form/TaskType.php - namespace AppBundle\Form; - - use Symfony\Component\Form\AbstractType; - use Symfony\Component\Form\FormBuilderInterface; + use AppBundle\Form\DataTransformer\IssueToNumberTransformer; class TaskType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder - ->add('task') - ->add('dueDate', null, array('widget' => 'single_text')) + ->add('description', 'textarea') ->add('issue', 'issue_selector'); } - public function getName() - { - return 'task'; - } + // ... } + +About Model and View Transformers +--------------------------------- + +In the above example, the transformer was used as a "model" transformer. +In fact, there are two different types of transformers and three different +types of underlying data. + +.. image:: /images/cookbook/form/DataTransformersTypes.png + :align: center + +In any form, the three different types of data are: + +1) **Model data** - This is the data in the format used in your application + (e.g. an ``Issue`` object). If you call ``Form::getData`` or ``Form::setData``, + you're dealing with the "model" data. + +2) **Norm Data** - This is a normalized version of your data, and is commonly + the same as your "model" data (though not in our example). It's not commonly + used directly. + +3) **View Data** - This is the format that's used to fill in the form fields + themselves. It's also the format in which the user will submit the data. When + you call ``Form::submit($data)``, the ``$data`` is in the "view" data format. + +The two different types of transformers help convert to and from each of these +types of data: + +**Model transformers**: + - ``transform``: "model data" => "norm data" + - ``reverseTransform``: "norm data" => "model data" + +**View transformers**: + - ``transform``: "norm data" => "view data" + - ``reverseTransform``: "view data" => "norm data" + +Which transformer you need depends on your situation. + +To use the view transformer, call ``addViewTransformer``. + +So why Use the Model Transformer? +--------------------------------- + +In this example, the field is a ``text`` field, and a text field is always +expected to be a simple, scalar format in the "norm" and "view" formats. For +this reason, the most appropriate transformer was the "model" transformer +(which converts to/from the *norm* format - string issue number - to the *model* +format - Issue object). + +The difference between the transformers is subtle and you should always think +about what the "norm" data for a field should really be. For example, the +"norm" data for a ``text`` field is a string, but is a ``DateTime`` object +for a ``date`` field. From 9be07f0b77edf3d35bb917a90cb9b3196490718f Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 28 Jun 2015 12:25:40 -0400 Subject: [PATCH 0312/2667] Many tweaks thanks to review --- cookbook/form/data_transformers.rst | 63 ++++++++++++++++------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index 5ae723232c5..d6e102e5f89 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -22,8 +22,11 @@ Simple Example: Sanitizing HTML on User Input Suppose you have a Task form with a description ``textarea`` type:: // src/AppBundle/Form/TaskType.php - // ... + use Symfony\Component\Form\FormBuilderInterface; + use Symfony\Component\OptionsResolver\OptionsResolverInterface; + + // ... class TaskType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) @@ -45,7 +48,7 @@ Suppose you have a Task form with a description ``textarea`` type:: But, there are two complications: #. Your users are allowed to use *some* HTML tags, but not others: you need a way - to call :phpfunction:`striptags` after the form is submitted. + to call :phpfunction:`striptags` after the form is submitted; #. To be friendly, you want to convert ``
`` tags into line breaks (``\n``) before rendering the field so the text is easier to edit. @@ -108,8 +111,8 @@ issue number. Start by setting up the text field like normal:: // src/AppBundle/Form/TaskType.php - // ... + // ... class TaskType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) @@ -134,7 +137,7 @@ property would be a string (e.g. "55"). How can you transform this into an ``Iss entity on submit? Creating the Transformer ------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~ You could use the ``CallbackTransformer`` like earlier. But since this is a bit more complex, creating a new transformer class will keep the ``TaskType`` form class simpler. @@ -146,17 +149,17 @@ to and from the issue number and the ``Issue`` object:: namespace AppBundle\Form\DataTransformer; use AppBundle\Entity\Issue; - use Doctrine\Common\Persistence\ObjectManager; + use Doctrine\Common\Persistence\EntityManager; use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\Exception\TransformationFailedException; class IssueToNumberTransformer implements DataTransformerInterface { - private $em; + private $entityManager; - public function __construct(ObjectManager $em) + public function __construct(EntityManager $entityManager) { - $this->em = $em; + $this->entityManager = $entityManager; } /** @@ -185,10 +188,10 @@ to and from the issue number and the ``Issue`` object:: { // no issue number? It's optional, so that's ok if (!$issueNumber) { - return null; + return; } - $issue = $this->em + $issue = $this->entityManager ->getRepository('AppBundle:Issue') // query for the issue with this id ->find($issueNumber) @@ -225,7 +228,7 @@ that message with the ``invalid_message`` option (see below). an empty string, 0 for integers or 0.0 for floats). Using the Transformer ---------------------- +~~~~~~~~~~~~~~~~~~~~~ Next, you need to instantiate the ``IssueToNumberTransformer`` class from inside ``TaskType`` and add it to the ``issue`` field. But to do that, you'll need an instance @@ -235,16 +238,18 @@ No problem! Just add a ``__construct()`` function to ``TaskType`` and force this to be passed in. Then, you can easily create and add the transformer:: // src/AppBundle/Form/TaskType.php + use AppBundle\Form\DataTransformer\IssueToNumberTransformer; - use Doctrine\Common\Persistence\ObjectManager; + use Doctrine\Common\Persistence\EntityManager; + // ... class TaskType extends AbstractType { - private $em; + private $entityManager; - public function __construct(ObjectManager $em) + public function __construct(EntityManager $entityManager) { - $this->em = $em; + $this->entityManager = $entityManager; } public function buildForm(FormBuilderInterface $builder, array $options) @@ -259,7 +264,7 @@ to be passed in. Then, you can easily create and add the transformer:: // ... $builder->get('issue') - ->addModelTransformer(new IssueToNumberTransformer($this->em)); + ->addModelTransformer(new IssueToNumberTransformer($this->entityManager)); } // ... @@ -268,8 +273,8 @@ to be passed in. Then, you can easily create and add the transformer:: Now, when you create your ``TaskType``, you'll need to pass in the entity manager:: // e.g. in a controller somewhere - $em = $this->getDoctrine()->getManager(); - $form = $this->createForm(new TaskType($em), $task); + $entityManager = $this->getDoctrine()->getManager(); + $form = $this->createForm(new TaskType($entityManager), $task); // ... @@ -312,23 +317,23 @@ First, create the custom field type class:: namespace AppBundle\Form; use AppBundle\Form\DataTransformer\IssueToNumberTransformer; - use Doctrine\Common\Persistence\ObjectManager; + use Doctrine\ORM\EntityManager; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; class IssueSelectorType extends AbstractType { - private $em; + private $entityManager; - public function __construct(ObjectManager $em) + public function __construct(EntityManager $entityManager) { - $this->em = $em; + $this->entityManager = $entityManager; } public function buildForm(FormBuilderInterface $builder, array $options) { - $transformer = new IssueToNumberTransformer($this->em); + $transformer = new IssueToNumberTransformer($this->entityManager); $builder->addModelTransformer($transformer); } @@ -423,15 +428,15 @@ types of underlying data. In any form, the three different types of data are: -1) **Model data** - This is the data in the format used in your application - (e.g. an ``Issue`` object). If you call ``Form::getData`` or ``Form::setData``, +#. **Model data** - This is the data in the format used in your application + (e.g. an ``Issue`` object). If you call ``Form::getData()`` or ``Form::setData()``, you're dealing with the "model" data. -2) **Norm Data** - This is a normalized version of your data, and is commonly +#. **Norm Data** - This is a normalized version of your data and is commonly the same as your "model" data (though not in our example). It's not commonly used directly. -3) **View Data** - This is the format that's used to fill in the form fields +#. **View Data** - This is the format that's used to fill in the form fields themselves. It's also the format in which the user will submit the data. When you call ``Form::submit($data)``, the ``$data`` is in the "view" data format. @@ -463,3 +468,7 @@ The difference between the transformers is subtle and you should always think about what the "norm" data for a field should really be. For example, the "norm" data for a ``text`` field is a string, but is a ``DateTime`` object for a ``date`` field. + +.. tip:: + + As a general rule, the normalized data should contain as much information as possible. From 1514fddda25d4095c2c80b000856e2b20a89f576 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 28 Jun 2015 12:27:39 -0400 Subject: [PATCH 0313/2667] using the aliases entity manager service name --- cookbook/form/data_transformers.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index d6e102e5f89..e66d9a86c70 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -365,19 +365,20 @@ it's recognized as a custom field type: .. code-block:: yaml + # app/config/services.yml services: app.type.issue_selector: class: AppBundle\Form\IssueSelectorType - arguments: ["@doctrine.orm.default_entity_manager"] + arguments: ["@doctrine.orm.entity_manager"] tags: - { name: form.type, alias: issue_selector } .. code-block:: xml - + - + @@ -391,7 +392,7 @@ it's recognized as a custom field type: ->setDefinition('app.type.issue_selector', new Definition( 'AppBundle\Form\IssueSelectorType' ), array( - new Reference('doctrine.orm.default_entity_manager'), + new Reference('doctrine.orm.entity_manager'), )) ->addTag('form.type', array( 'alias' => 'issue_selector', From 41bb9078ac4cff6fff3c36122525ee0db75df8ca Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 28 Jun 2015 12:29:06 -0400 Subject: [PATCH 0314/2667] adding use statement --- cookbook/form/data_transformers.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index e66d9a86c70..9986073eec5 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -60,6 +60,7 @@ class:: // src/AppBundle/Form/TaskType.php use Symfony\Component\Form\CallbackTransformer; + use Symfony\Component\Form\FormBuilderInterface; // ... class TaskType extends AbstractType From 28d7f89a8341380257c2c582267a3ec86fcd7c64 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 28 Jun 2015 12:30:27 -0400 Subject: [PATCH 0315/2667] fixed typo thanks to @OskarStark --- cookbook/form/data_transformers.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index 9986073eec5..ac607335f11 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -100,8 +100,8 @@ in your code. :class:`Symfony\\Component\\Form\\DataTransformerInterface` - so you can create your own classes, instead of putting all the logic in the form (see the next section). -Harder Examle: Transforming an Issue Number into an Issue Entity ----------------------------------------------------------------- +Harder Example: Transforming an Issue Number into an Issue Entity +----------------------------------------------------------------- Say you have a many-to-one relation from the Task entity to an Issue entity (i.e. each Task has an optional foreign key to its related Issue). Adding a listbox with all From 0762848fa4ae0c658d9fefc1c2981f528a699be7 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 8 Jul 2015 17:53:16 -0400 Subject: [PATCH 0316/2667] Adding an example of how the other format for attaching transformers looks --- cookbook/form/data_transformers.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index ac607335f11..cfc8cf1035b 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -100,6 +100,14 @@ in your code. :class:`Symfony\\Component\\Form\\DataTransformerInterface` - so you can create your own classes, instead of putting all the logic in the form (see the next section). +You can also add the transformer, right when adding the field by changing the format +slightly:: + + $builder->add( + $builder->create('description', 'textarea') + ->addModelTransformer(...) + ); + Harder Example: Transforming an Issue Number into an Issue Entity ----------------------------------------------------------------- From c8f484452b9847e983cd36e8974a043e411865e2 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 8 Jul 2015 18:01:27 -0400 Subject: [PATCH 0317/2667] [#5456] Updating for Symfony 2.7 --- cookbook/form/data_transformers.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index 19767001473..8b9ce662f1f 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -24,7 +24,7 @@ Suppose you have a Task form with a description ``textarea`` type:: // src/AppBundle/Form/TaskType.php use Symfony\Component\Form\FormBuilderInterface; - use Symfony\Component\OptionsResolver\OptionsResolverInterface; + use Symfony\Component\OptionsResolver\OptionsResolver; // ... class TaskType extends AbstractType @@ -35,7 +35,7 @@ Suppose you have a Task form with a description ``textarea`` type:: ->add('description', 'textarea'); } - public function setDefaultOptions(OptionsResolverInterface $resolver) + public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'data_class' => 'AppBundle\Entity\Task' @@ -131,7 +131,7 @@ Start by setting up the text field like normal:: ->add('issue', 'text'); } - public function setDefaultOptions(OptionsResolverInterface $resolver) + public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'data_class' => 'AppBundle\Entity\Task' From 33e5613582a602606643095b5fa9bc957c1c6d2a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 9 Jul 2015 08:43:11 +0200 Subject: [PATCH 0318/2667] some tweaks to the data transformers chapter * updates some code blocks * adds labels to keep BC for old headlines --- cookbook/form/data_transformers.rst | 64 ++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index cfc8cf1035b..ac0ccd8b0f5 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -22,6 +22,7 @@ Simple Example: Sanitizing HTML on User Input Suppose you have a Task form with a description ``textarea`` type:: // src/AppBundle/Form/TaskType.php + namespace AppBundle\Form\Type; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; @@ -31,14 +32,13 @@ Suppose you have a Task form with a description ``textarea`` type:: { public function buildForm(FormBuilderInterface $builder, array $options) { - $builder - ->add('description', 'textarea'); + $builder->add('description', 'textarea'); } - + public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( - 'data_class' => 'AppBundle\Entity\Task' + 'data_class' => 'AppBundle\Entity\Task', )); } @@ -58,6 +58,7 @@ field. The easiest way to do this is with the :class:`Symfony\\Component\\Form\\ class:: // src/AppBundle/Form/TaskType.php + namespace AppBundle\Form\Type; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\FormBuilderInterface; @@ -67,8 +68,7 @@ class:: { public function buildForm(FormBuilderInterface $builder, array $options) { - $builder - ->add('description', 'textarea'); + $builder->add('description', 'textarea'); $builder->get('description') ->addModelTransformer(new CallbackTransformer( @@ -83,7 +83,8 @@ class:: // transform any \n to real
return str_replace("\n", '
', $cleaned); } - )); + )) + ; } // ... @@ -120,6 +121,7 @@ issue number. Start by setting up the text field like normal:: // src/AppBundle/Form/TaskType.php + namespace AppBundle\Form\Type; // ... class TaskType extends AbstractType @@ -128,7 +130,8 @@ Start by setting up the text field like normal:: { $builder ->add('description', 'textarea') - ->add('issue', 'text'); + ->add('issue', 'text') + ; } public function setDefaultOptions(OptionsResolverInterface $resolver) @@ -247,6 +250,7 @@ No problem! Just add a ``__construct()`` function to ``TaskType`` and force this to be passed in. Then, you can easily create and add the transformer:: // src/AppBundle/Form/TaskType.php + namespace AppBundle\Form\Type; use AppBundle\Form\DataTransformer\IssueToNumberTransformer; use Doctrine\Common\Persistence\EntityManager; @@ -267,7 +271,7 @@ to be passed in. Then, you can easily create and add the transformer:: ->add('description', 'textarea') ->add('issue', 'text', array( // validation message if the data transformer fails - 'invalid_message' => 'That is not a valid issue number' + 'invalid_message' => 'That is not a valid issue number', )); // ... @@ -311,6 +315,8 @@ its error message can be controlled with the ``invalid_message`` field option. $builder->add('issue', 'text') ->addModelTransformer($transformer); +.. _using-transformers-in-a-custom-field-type: + Creating a Reusable issue_selector Field ---------------------------------------- @@ -322,7 +328,6 @@ that does this automatically. First, create the custom field type class:: // src/AppBundle/Form/IssueSelectorType.php - namespace AppBundle\Form; use AppBundle\Form\DataTransformer\IssueToNumberTransformer; @@ -385,24 +390,37 @@ it's recognized as a custom field type: .. code-block:: xml - - - - + + + + + + + + + + + .. code-block:: php + // app/config/services.php use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; // ... $container ->setDefinition('app.type.issue_selector', new Definition( - 'AppBundle\Form\IssueSelectorType' - ), array( - new Reference('doctrine.orm.entity_manager'), - )) + 'AppBundle\Form\IssueSelectorType' + ), + array( + new Reference('doctrine.orm.entity_manager'), + ) + ) ->addTag('form.type', array( 'alias' => 'issue_selector', )) @@ -412,7 +430,10 @@ Now, whenever you need to use your special ``issue_selector`` field type, it's quite easy:: // src/AppBundle/Form/TaskType.php + namespace AppBundle\Form\Type; + use AppBundle\Form\DataTransformer\IssueToNumberTransformer; + // ... class TaskType extends AbstractType { @@ -420,12 +441,15 @@ it's quite easy:: { $builder ->add('description', 'textarea') - ->add('issue', 'issue_selector'); + ->add('issue', 'issue_selector') + ; } // ... } +.. _model-and-view-transformers: + About Model and View Transformers --------------------------------- From 074441571e75aff5767d49e50b215ff8a5643836 Mon Sep 17 00:00:00 2001 From: Chris Sedlmayr Date: Thu, 9 Jul 2015 09:00:17 +0100 Subject: [PATCH 0319/2667] Fixes small typo in data transformers cookbook --- cookbook/form/data_transformers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index cfc8cf1035b..0c9284ff03b 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -89,7 +89,7 @@ class:: // ... } -The ``CallbackTransformer`` takes to callback functions as arguments. The first transforms +The ``CallbackTransformer`` takes two callback functions as arguments. The first transforms the original value into a format that'll be used to render the field. The second does the reverse: it transforms the submitted value back into the format you'll use in your code. From 363b794309473778fedd6864b7d392acd84d29e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 10 Jul 2015 08:12:58 +0200 Subject: [PATCH 0320/2667] [PSR-7] Fix Diactoros link --- cookbook/psr7.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cookbook/psr7.rst b/cookbook/psr7.rst index 2d0316d1824..79cab6caa09 100644 --- a/cookbook/psr7.rst +++ b/cookbook/psr7.rst @@ -17,7 +17,7 @@ You can install the component in 2 different ways: * Use the official Git repository (https://github.com/symfony/psr-http-message-bridge). The bridge also needs a PSR-7 implementation to allow converting HttpFoundation -objects to PSR-7 objects. It provides native support for _`Zend Diactoros`_. +objects to PSR-7 objects. It provides native support for `Zend Diactoros`_. Use Composer (``zendframework/zend-diactoros`` on `Packagist`_) or refer to the project documentation to install it. @@ -86,3 +86,4 @@ to a :class:`Symfony\\Component\\HttpFoundation\\Response` instance:: .. _`PSR-7`: http://www.php-fig.org/psr/psr-7/ .. _Packagist: https://packagist.org/packages/symfony/psr-http-message-bridge +.. _`Zend Diactoros`: https://github.com/zendframework/zend-diactoros From 6df293c4824dad7457526292c486d5f93ead14ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 29 May 2015 12:31:48 +0200 Subject: [PATCH 0321/2667] [PSR-7] Bridge documentation --- cookbook/index.rst | 1 + cookbook/map.rst.inc | 4 ++ cookbook/psr7.rst | 88 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 cookbook/psr7.rst diff --git a/cookbook/index.rst b/cookbook/index.rst index 98828a56287..5d5a2797c3f 100644 --- a/cookbook/index.rst +++ b/cookbook/index.rst @@ -27,6 +27,7 @@ The Cookbook serializer service_container/index session/index + psr7 symfony1 templating/index testing/index diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 74eb60224c4..243d8e9daed 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -192,6 +192,10 @@ * (configuration) :doc:`/cookbook/configuration/pdo_session_storage` * :doc:`/cookbook/session/avoid_session_start` +* **PSR-7** + + * :doc:`/cookbook/psr7` + * **symfony1** * :doc:`/cookbook/symfony1` diff --git a/cookbook/psr7.rst b/cookbook/psr7.rst new file mode 100644 index 00000000000..ece2040850b --- /dev/null +++ b/cookbook/psr7.rst @@ -0,0 +1,88 @@ +.. index:: + single: PSR-7 + +The PSR-7 Bridge +================ + + The PSR-7 bridge converts :doc:`HttpFoundation ` + objects from and to objects implementing HTTP message interfaces defined + by the `PSR-7`_. + +Installation +------------ + +You can install the component in 2 different ways: + +* :doc:`Install it via Composer ` (``symfony/psr-http-message-bridge`` on `Packagist`_); +* Use the official Git repository (https://github.com/symfony/psr-http-message-bridge). + +The bridge also needs a PSR-7 implementation to allow converting HttpFoundation +objects to PSR-7 objects. It provides native support for _`Zend Diactoros`_. +Use Composer (``zendframework/zend-diactoros`` on `Packagist`_) or refers to +the project documentation to install it. + +Usage +----- + +Converting from HttpFoundation Objects to PSR-7 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The bridge provides an interface of a factory called +:class:`Symfony\\Bridge\\PsrHttpMessage\\HttpMessageFactoryInterface` +that builds objects implementing PSR-7 interfaces from HttpFoundation objects. +It also provide a default implementation using Zend Diactoros internally. + +The following code snippet explain how to convert a :class:`Symfony\\Component\\HttpFoundation\\Request` +to a Zend Diactoros :class:`Zend\\Diactoros\\ServerRequest` implementing the +:class:`Psr\\Http\\Message\\ServerRequestInterface` interface:: + + use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory; + use Symfony\Component\HttpFoundation\Request; + + $symfonyRequest = new Request(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'dunglas.fr'), 'Content'); + // The HTTP_HOST server key must be set to avoid an unexpected error + + $psr7Factory = new DiactorosFactory(); + $psrRequest = $psr7Factory->createRequest($symfonyRequest); + +And now from a :class:`Symfony\\Component\\HttpFoundation\\Response` to a Zend +Diactoros :class:`Zend\\Diactoros\\Response` implementing the :class:`Psr\\Http\\Message\\ResponseInterface` +interface:: + + use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory; + use Symfony\Component\HttpFoundation\Response; + + $symfonyResponse = new Response('Content'); + + $psr7Factory = new DiactorosFactory(); + $psrResponse = $psr7Factory->createResponse($symfonyResponse); + +Converting Objects implementing PSR-7 Interfaces to HttpFoundation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +On the other hand, the bridge provide a factory interface called +:class:`Symfony\\Bridge\\PsrHttpMessage\\HttpFoundationFactoryInterface` +that builds HttpFoundation objects from objects implementing PSR-7 interfaces. + +The next snippet explain how to convert an object implementing the :class:`Psr\\Http\\Message\\ServerRequestInterface` +interface to a :class:`Symfony\\Component\\HttpFoundation\\Request` instance:: + + use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; + + // $psrRequest is an instance of Psr\Http\Message\ServerRequestInterface + + $httpFoundationFactory = new HttpFoundationFactory(); + $symfonyRequest = $httpFoundationFactory->createRequest($psrRequest); + +From an object implementing the :class:`Psr\\Http\\Message\\ResponseInterface` +to a :class:`Symfony\\Component\\HttpFoundation\\Response` instance:: + + use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; + + // $psrResponse is an instance of Psr\Http\Message\ResponseInterface + + $httpFoundationFactory = new HttpFoundationFactory(); + $symfonyResponse = $httpFoundationFactory->createResponse($psrResponse); + +.. _`PSR-7`: http://www.php-fig.org/psr/psr-7/ +.. _Packagist: https://packagist.org/packages/symfony/psr-http-message-bridge From c6f10c99a277a25f7af669c701f452fe96382acd Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 19 Jun 2015 12:24:17 -0400 Subject: [PATCH 0322/2667] [#5331] Tiny typo --- cookbook/psr7.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/psr7.rst b/cookbook/psr7.rst index ece2040850b..2d0316d1824 100644 --- a/cookbook/psr7.rst +++ b/cookbook/psr7.rst @@ -18,7 +18,7 @@ You can install the component in 2 different ways: The bridge also needs a PSR-7 implementation to allow converting HttpFoundation objects to PSR-7 objects. It provides native support for _`Zend Diactoros`_. -Use Composer (``zendframework/zend-diactoros`` on `Packagist`_) or refers to +Use Composer (``zendframework/zend-diactoros`` on `Packagist`_) or refer to the project documentation to install it. Usage From fe9759aa83db094b1fe42524ccdfe8fe06c86336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 10 Jul 2015 08:12:58 +0200 Subject: [PATCH 0323/2667] [PSR-7] Fix Diactoros link --- cookbook/psr7.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cookbook/psr7.rst b/cookbook/psr7.rst index 2d0316d1824..79cab6caa09 100644 --- a/cookbook/psr7.rst +++ b/cookbook/psr7.rst @@ -17,7 +17,7 @@ You can install the component in 2 different ways: * Use the official Git repository (https://github.com/symfony/psr-http-message-bridge). The bridge also needs a PSR-7 implementation to allow converting HttpFoundation -objects to PSR-7 objects. It provides native support for _`Zend Diactoros`_. +objects to PSR-7 objects. It provides native support for `Zend Diactoros`_. Use Composer (``zendframework/zend-diactoros`` on `Packagist`_) or refer to the project documentation to install it. @@ -86,3 +86,4 @@ to a :class:`Symfony\\Component\\HttpFoundation\\Response` instance:: .. _`PSR-7`: http://www.php-fig.org/psr/psr-7/ .. _Packagist: https://packagist.org/packages/symfony/psr-http-message-bridge +.. _`Zend Diactoros`: https://github.com/zendframework/zend-diactoros From 6a9fe713232b8cc4112aa55a3c07ffac55d91e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 10 Jul 2015 08:32:49 +0200 Subject: [PATCH 0324/2667] [HttpKernel] Fix use statement --- components/http_kernel/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/http_kernel/introduction.rst b/components/http_kernel/introduction.rst index ae3cf59fcef..6b83bb4f04f 100644 --- a/components/http_kernel/introduction.rst +++ b/components/http_kernel/introduction.rst @@ -691,7 +691,7 @@ can be used to figure out if the current request is a "master" or "sub" request. For example, a listener that only needs to act on the master request may look like this:: - use Symfony\Component\HttpKernel\HttpKernelInterface; + use Symfony\Component\HttpKernel\Event\GetResponseEvent; // ... public function onKernelRequest(GetResponseEvent $event) From 1a4b5fab1992c931ca271d53a16e9836e1b46dce Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 10 Jul 2015 13:19:25 +0200 Subject: [PATCH 0325/2667] Reword --- components/security/secure_tools.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/components/security/secure_tools.rst b/components/security/secure_tools.rst index c0e6965efb9..924b0795c29 100644 --- a/components/security/secure_tools.rst +++ b/components/security/secure_tools.rst @@ -60,8 +60,9 @@ to work correctly. Just pass a file name to enable it:: .. tip:: The ``nextBytes()`` method returns a binary string which may contain the - ``\0`` character. If you store this value in a database or include it as - part of the URL, make sure to hash the value returned by ``nextBytes()`` - (to do that, you can use a simple ``md5()`` PHP function). + ``\0`` character. This can cause troubles in lots of common scenarios, such + as storing this value in a database or including it as part of the URL. The + solution is to hash the value returned by ``nextBytes()`` (to do that, you + can use a simple ``md5()`` PHP function). .. _`Timing attack`: http://en.wikipedia.org/wiki/Timing_attack From 46f3665bfa89379abf211f03bedadc52edc4733c Mon Sep 17 00:00:00 2001 From: wouthoekstra Date: Fri, 5 Jun 2015 17:24:00 +0200 Subject: [PATCH 0326/2667] Update http_fundamentals.rst Added some nuance to note about PUT and DELETE in modern browsers. --- book/http_fundamentals.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/book/http_fundamentals.rst b/book/http_fundamentals.rst index 19d8df030da..b6441df88b8 100644 --- a/book/http_fundamentals.rst +++ b/book/http_fundamentals.rst @@ -98,7 +98,9 @@ delete a specific blog entry, for example: There are actually nine HTTP methods defined by the HTTP specification, but many of them are not widely used or supported. In reality, many modern - browsers don't even support the ``PUT`` and ``DELETE`` methods. + browsers only support ``POST`` and ``GET`` in HTML forms. + The methods ``PUT`` and ``DELETE`` are supported in XMLHttpRequests, + which are commonly used in AJAX calls. In addition to the first line, an HTTP request invariably contains other lines of information called request headers. The headers can supply a wide From 1bc5228a9cff538dba2a524478c2934e35bbcd81 Mon Sep 17 00:00:00 2001 From: wouthoekstra Date: Mon, 8 Jun 2015 22:31:36 +0200 Subject: [PATCH 0327/2667] Updated text about HTTP methods --- book/http_fundamentals.rst | 10 +++++----- cookbook/routing/method_parameters.rst | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/book/http_fundamentals.rst b/book/http_fundamentals.rst index b6441df88b8..fb985d6c395 100644 --- a/book/http_fundamentals.rst +++ b/book/http_fundamentals.rst @@ -96,11 +96,11 @@ delete a specific blog entry, for example: .. note:: - There are actually nine HTTP methods defined by the HTTP specification, - but many of them are not widely used or supported. In reality, many modern - browsers only support ``POST`` and ``GET`` in HTML forms. - The methods ``PUT`` and ``DELETE`` are supported in XMLHttpRequests, - which are commonly used in AJAX calls. + There are actually nine HTTP methods (also known as verbs) defined by + the HTTP specification, but many of them are not widely used or supported. + In reality, many modern browsers only support ``POST`` and ``GET`` in + HTML forms. Various others are however supported in XMLHttpRequests, + as well as by Symfony's router. In addition to the first line, an HTTP request invariably contains other lines of information called request headers. The headers can supply a wide diff --git a/cookbook/routing/method_parameters.rst b/cookbook/routing/method_parameters.rst index 6da564f18ce..b8ceeaa18a9 100644 --- a/cookbook/routing/method_parameters.rst +++ b/cookbook/routing/method_parameters.rst @@ -83,7 +83,7 @@ Faking the Method with ``_method`` 2.3, use the :ref:`configuration-framework-http_method_override` option. Unfortunately, life isn't quite this simple, since most browsers do not -support sending PUT and DELETE requests. Fortunately, Symfony provides you +support sending PUT and DELETE requests as method in HTML forms. Fortunately, Symfony provides you with a simple way of working around this limitation. By including a ``_method`` parameter in the query string or parameters of an HTTP request, Symfony will use this as the method when matching routes. Forms automatically include a From f9ef3ee179067cdb550fcd8ac045ee166c694ae8 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 12 Jul 2015 09:44:17 -0400 Subject: [PATCH 0328/2667] [#5352] minor language tweak --- cookbook/routing/method_parameters.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cookbook/routing/method_parameters.rst b/cookbook/routing/method_parameters.rst index b8ceeaa18a9..be270240dd2 100644 --- a/cookbook/routing/method_parameters.rst +++ b/cookbook/routing/method_parameters.rst @@ -82,11 +82,11 @@ Faking the Method with ``_method`` before you handle the request (e.g. in your front controller). In Symfony 2.3, use the :ref:`configuration-framework-http_method_override` option. -Unfortunately, life isn't quite this simple, since most browsers do not -support sending PUT and DELETE requests as method in HTML forms. Fortunately, Symfony provides you -with a simple way of working around this limitation. By including a ``_method`` -parameter in the query string or parameters of an HTTP request, Symfony will -use this as the method when matching routes. Forms automatically include a +Unfortunately, life isn't quite this simple, since most browsers do not support +sending PUT and DELETE requests via the `method` attribute in an HTML form. Fortunately, +Symfony provides you with a simple way of working around this limitation. By including +a ``_method`` parameter in the query string or parameters of an HTTP request, Symfony +will use this as the method when matching routes. Forms automatically include a hidden field for this parameter if their submission method is not GET or POST. See :ref:`the related chapter in the forms documentation` for more information. From 7876d7e5a85688ab18f45423adba63aea26b1d3c Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 14 Jul 2015 18:11:36 +0900 Subject: [PATCH 0329/2667] Fix RST --- create_framework/routing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_framework/routing.rst b/create_framework/routing.rst index 6bbb46f64b8..c9456b02b04 100644 --- a/create_framework/routing.rst +++ b/create_framework/routing.rst @@ -158,7 +158,7 @@ With this knowledge in mind, let's write the new version of our framework:: $response->send(); -There are a few new things in the code:: +There are a few new things in the code: * Route names are used for template names; From e603430d8a6b469d5688d961e7e2a16f85391cf2 Mon Sep 17 00:00:00 2001 From: David Baucum Date: Mon, 13 Jul 2015 11:42:00 -0400 Subject: [PATCH 0330/2667] Minor grammar fix. --- book/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/installation.rst b/book/installation.rst index 37e89431904..e7740ce2a41 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -127,7 +127,7 @@ based on `Composer`_. Composer is the dependency manager used by modern PHP applications and it can also be used to create new applications based on the Symfony Framework. If you -don't have installed it globally, start by reading the next section. +don't have it installed globally, start by reading the next section. Installing Composer Globally ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 1524f5a66ec34bbaefad6802aec3163a4b2eedbd Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 14 Jul 2015 21:34:50 +0200 Subject: [PATCH 0331/2667] Fixed an error in the auto_alias format value --- reference/dic_tags.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 9788888e43b..56013b4a52e 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -297,7 +297,7 @@ the generic ``app.lock`` service can be defined as follows: public: false app.lock: tags: - - { name: auto_alias, format: "app.%database_type%.lock" } + - { name: auto_alias, format: "app.%database_type%_lock" } .. code-block:: xml @@ -315,7 +315,7 @@ the generic ``app.lock`` service can be defined as follows: class="AppBundle\Lock\SqliteLock" /> - + @@ -328,7 +328,7 @@ the generic ``app.lock`` service can be defined as follows: ->register('app.sqlite_lock', 'AppBundle\Lock\SqliteLock')->setPublic(false) ->register('app.lock') - ->addTag('auto_alias', array('format' => 'app.%database_type%.lock')) + ->addTag('auto_alias', array('format' => 'app.%database_type%_lock')) ; The ``format`` parameter defines the expression used to construct the name of From 0320b5e5ab8fe627e828339f86571228abba163b Mon Sep 17 00:00:00 2001 From: Schuyler Jager Date: Mon, 16 Mar 2015 16:57:52 -0400 Subject: [PATCH 0332/2667] Update gmail.rst There are issues with the current docs as they don't address certain user configurations that prevent Symfony from emailing from their app. Mentioning two factor authentication and the less secure app setting is in my opinion, paramount. Reference: http://stackoverflow.com/q/29085617/1188035 --- cookbook/email/gmail.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cookbook/email/gmail.rst b/cookbook/email/gmail.rst index 6a64a82e1ea..1898e7c9b29 100644 --- a/cookbook/email/gmail.rst +++ b/cookbook/email/gmail.rst @@ -75,3 +75,9 @@ You're done! The ``gmail`` transport is simply a shortcut that uses the ``smtp`` transport and sets ``encryption``, ``auth_mode`` and ``host`` to work with Gmail. + +.. note:: + + Depending on your Gmail account settings, you may get authentication errors within your app. + You should ensure two-factor authentication and [also allow less secure apps to access your + account](https://support.google.com/accounts/answer/6010255) From 60703ba21094249f4db9f20bc8ed1c635e03e738 Mon Sep 17 00:00:00 2001 From: Schuyler Jager Date: Wed, 25 Mar 2015 14:45:28 -0400 Subject: [PATCH 0333/2667] Revisions to the gmail.rst doc Revisions concerning the 2-Step-Verification and correct documentation syntax. Thanks @WouterJ and @javiereguiluz --- cookbook/email/gmail.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cookbook/email/gmail.rst b/cookbook/email/gmail.rst index 1898e7c9b29..7524847b62a 100644 --- a/cookbook/email/gmail.rst +++ b/cookbook/email/gmail.rst @@ -79,5 +79,9 @@ You're done! .. note:: Depending on your Gmail account settings, you may get authentication errors within your app. - You should ensure two-factor authentication and [also allow less secure apps to access your - account](https://support.google.com/accounts/answer/6010255) + If your Gmail account uses 2-Step-Verification, you should `generate an App password`_ to use for your + ``mailer_password`` parameter. + You should also ensure that you `allow less secure apps to access your Gmail account`_. + +.. _`generate an App password`: https://support.google.com/accounts/answer/185833 +.. _`also allow less secure apps to access your account`: https://support.google.com/accounts/answer/6010255 From eb16061199480a212749207afa8b4a5a53a22ea4 Mon Sep 17 00:00:00 2001 From: Schuyler Jager Date: Wed, 25 Mar 2015 14:46:25 -0400 Subject: [PATCH 0334/2667] Reflect links correctly in gmail.rst --- cookbook/email/gmail.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/email/gmail.rst b/cookbook/email/gmail.rst index 7524847b62a..637bc7b6445 100644 --- a/cookbook/email/gmail.rst +++ b/cookbook/email/gmail.rst @@ -84,4 +84,4 @@ You're done! You should also ensure that you `allow less secure apps to access your Gmail account`_. .. _`generate an App password`: https://support.google.com/accounts/answer/185833 -.. _`also allow less secure apps to access your account`: https://support.google.com/accounts/answer/6010255 +.. _`allow less secure apps to access your Gmail account`: https://support.google.com/accounts/answer/6010255 From bafbd87fd24aa81bfd4f912b1bb25f95d6b05b28 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 23 Jun 2015 17:29:46 +0200 Subject: [PATCH 0335/2667] Fixed some minor syntax issues. --- cookbook/email/gmail.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cookbook/email/gmail.rst b/cookbook/email/gmail.rst index 637bc7b6445..9f925cad224 100644 --- a/cookbook/email/gmail.rst +++ b/cookbook/email/gmail.rst @@ -78,10 +78,10 @@ You're done! .. note:: - Depending on your Gmail account settings, you may get authentication errors within your app. - If your Gmail account uses 2-Step-Verification, you should `generate an App password`_ to use for your - ``mailer_password`` parameter. + Depending on your Gmail account settings, you may get authentication errors + within your app. If your Gmail account uses 2-Step-Verification, you should + `generate an App password`_ to use for your ``mailer_password`` parameter. You should also ensure that you `allow less secure apps to access your Gmail account`_. - + .. _`generate an App password`: https://support.google.com/accounts/answer/185833 .. _`allow less secure apps to access your Gmail account`: https://support.google.com/accounts/answer/6010255 From 28b98b98c70466289b3d840a636b184d6cef3cbd Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jun 2015 10:26:14 +0200 Subject: [PATCH 0336/2667] Added a note about the implementation of the verbosity semantic methods --- components/console/introduction.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 8bf3003d6f8..466e4d24869 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -223,6 +223,13 @@ verbosity levels:: // ... } +.. note:: + + For backwards compatibility reasons, these semantic methods do not exist in + the ``OutputInterface`` class until Symfony 3.0. They are defined in the + different implementations of the interface + (e.g. :class:`Symfony\\Component\\Console\\Output\\Output`). + When the quiet level is used, all output is suppressed as the default :method:`Symfony\\Component\\Console\\Output\\Output::write` method returns without actually printing. From a88d1c66a6e675ba2c3216a26f63b26dd5e63a77 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 30 Jun 2015 16:09:38 +0200 Subject: [PATCH 0337/2667] Reworded a note about BC --- components/console/introduction.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 466e4d24869..37fe38fac0e 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -225,10 +225,10 @@ verbosity levels:: .. note:: - For backwards compatibility reasons, these semantic methods do not exist in - the ``OutputInterface`` class until Symfony 3.0. They are defined in the - different implementations of the interface - (e.g. :class:`Symfony\\Component\\Console\\Output\\Output`). + These semantic methods are defined in the ``OutputInterface`` starting from + Symfony 3.0. In previous Symfony versions they are defined in the different + implementations of the interface (e.g. :class:`Symfony\\Component\\Console\\Output\\Output`) + in order to keep backwards compatibility. When the quiet level is used, all output is suppressed as the default :method:`Symfony\\Component\\Console\\Output\\Output::write` method returns From 0905395774c40ecc94c4c1802e14bf0bf3efdd54 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 15 Jul 2015 20:20:06 +0900 Subject: [PATCH 0338/2667] Fix code --- create_framework/http-kernel-controller-resolver.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_framework/http-kernel-controller-resolver.rst b/create_framework/http-kernel-controller-resolver.rst index b0393b4cdd3..deca8f05abd 100644 --- a/create_framework/http-kernel-controller-resolver.rst +++ b/create_framework/http-kernel-controller-resolver.rst @@ -160,7 +160,7 @@ Let's conclude with the new version of our framework:: function render_template(Request $request) { - extract($request->attributes->all()); + extract($request->attributes->all(), EXTR_SKIP); ob_start(); include sprintf(__DIR__.'/../src/pages/%s.php', $_route); From 0ccc5664035c7a13e81959be975b4e01574ddb00 Mon Sep 17 00:00:00 2001 From: GuGuss Date: Mon, 13 Jul 2015 17:54:40 +0200 Subject: [PATCH 0339/2667] Prepare Platform.sh configuration files. --- .platform.app.yaml | 92 +++++++++++++++++++++++++++++++++++++++++ .platform/routes.yaml | 16 +++++++ .platform/services.yaml | 1 + 3 files changed, 109 insertions(+) create mode 100644 .platform.app.yaml create mode 100644 .platform/routes.yaml create mode 100644 .platform/services.yaml diff --git a/.platform.app.yaml b/.platform.app.yaml new file mode 100644 index 00000000000..bd4aa98a2ff --- /dev/null +++ b/.platform.app.yaml @@ -0,0 +1,92 @@ +# This file describes an application. You can have multiple applications +# in the same project. + +# The name of this app. Must be unique within a project. +name: php + +# The toolstack used to build the application. +type: "php" + +build: + flavor: "composer" + +# The configuration of app when it is exposed to the web. +web: + # The public directory of the app, relative to its root. + document_root: "/_build/html" + index_files: + - index.html + whitelist: + - \.html$ + + # CSS and Javascript. + - \.css$ + - \.js$ + - \.hbs$ + + # image/* types. + - \.gif$ + - \.jpe?g$ + - \.png$ + - \.tiff?$ + - \.wbmp$ + - \.ico$ + - \.jng$ + - \.bmp$ + - \.svgz?$ + + # audio/* types. + - \.midi?$ + - \.mpe?ga$ + - \.mp2$ + - \.mp3$ + - \.m4a$ + - \.ra$ + - \.weba$ + + # video/* types. + - \.3gpp?$ + - \.mp4$ + - \.mpe?g$ + - \.mpe$ + - \.ogv$ + - \.mov$ + - \.webm$ + - \.flv$ + - \.mng$ + - \.asx$ + - \.asf$ + - \.wmv$ + - \.avi$ + + # application/ogg. + - \.ogx$ + + # application/x-shockwave-flash. + - \.swf$ + + # application/java-archive. + - \.jar$ + + # fonts types. + - \.ttf$ + - \.eot$ + - \.woff$ + - \.otf$ + + # robots.txt. + - /robots\.txt$ + +# The size of the persistent disk of the application (in MB). +disk: 2048 + +# Build time dependencies. +dependencies: + python: + sphinx: "1.2.3" + +# The hooks that will be performed when the package 10000 is deployed. +hooks: + build: | + pip install git+https://github.com/fabpot/sphinx-php.git + make html diff --git a/.platform/routes.yaml b/.platform/routes.yaml new file mode 100644 index 00000000000..6e1db72d3ce --- /dev/null +++ b/.platform/routes.yaml @@ -0,0 +1,16 @@ +http://www.{default}/: + to: http://{default}/ + type: redirect +http://{default}/: + cache: + cookies: + - '*' + default_ttl: 0 + enabled: true + headers: + - Accept + - Accept-Language + ssi: + enabled: false + type: upstream + upstream: php:php diff --git a/.platform/services.yaml b/.platform/services.yaml new file mode 100644 index 00000000000..ec9369f2b00 --- /dev/null +++ b/.platform/services.yaml @@ -0,0 +1 @@ +# Keeping this file empty to not deploy unused services. From fdb77dbe1a76f836abc9440e85a2dbb042d13ea9 Mon Sep 17 00:00:00 2001 From: GuGuss Date: Tue, 14 Jul 2015 12:30:12 +0200 Subject: [PATCH 0340/2667] Improve configuration files. --- .platform.app.yaml | 43 +++---------------------------------------- .platform/routes.yaml | 2 +- 2 files changed, 4 insertions(+), 41 deletions(-) diff --git a/.platform.app.yaml b/.platform.app.yaml index bd4aa98a2ff..d163c3e44da 100644 --- a/.platform.app.yaml +++ b/.platform.app.yaml @@ -2,7 +2,7 @@ # in the same project. # The name of this app. Must be unique within a project. -name: php +name: symfonydocs # The toolstack used to build the application. type: "php" @@ -18,6 +18,7 @@ web: - index.html whitelist: - \.html$ + - \.txt$ # CSS and Javascript. - \.css$ @@ -26,48 +27,10 @@ web: # image/* types. - \.gif$ - - \.jpe?g$ - \.png$ - - \.tiff?$ - - \.wbmp$ - \.ico$ - - \.jng$ - - \.bmp$ - \.svgz?$ - # audio/* types. - - \.midi?$ - - \.mpe?ga$ - - \.mp2$ - - \.mp3$ - - \.m4a$ - - \.ra$ - - \.weba$ - - # video/* types. - - \.3gpp?$ - - \.mp4$ - - \.mpe?g$ - - \.mpe$ - - \.ogv$ - - \.mov$ - - \.webm$ - - \.flv$ - - \.mng$ - - \.asx$ - - \.asf$ - - \.wmv$ - - \.avi$ - - # application/ogg. - - \.ogx$ - - # application/x-shockwave-flash. - - \.swf$ - - # application/java-archive. - - \.jar$ - # fonts types. - \.ttf$ - \.eot$ @@ -78,7 +41,7 @@ web: - /robots\.txt$ # The size of the persistent disk of the application (in MB). -disk: 2048 +disk: 512 # Build time dependencies. dependencies: diff --git a/.platform/routes.yaml b/.platform/routes.yaml index 6e1db72d3ce..f99889ccec3 100644 --- a/.platform/routes.yaml +++ b/.platform/routes.yaml @@ -13,4 +13,4 @@ http://{default}/: ssi: enabled: false type: upstream - upstream: php:php + upstream: symfonydocs:php From 176f90144243aface68e97e60da66fbd6a9641b7 Mon Sep 17 00:00:00 2001 From: GuGuss Date: Tue, 14 Jul 2015 13:03:58 +0200 Subject: [PATCH 0341/2667] Better Sphinx version. --- .platform.app.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.platform.app.yaml b/.platform.app.yaml index d163c3e44da..946c6f7c152 100644 --- a/.platform.app.yaml +++ b/.platform.app.yaml @@ -46,7 +46,7 @@ disk: 512 # Build time dependencies. dependencies: python: - sphinx: "1.2.3" + sphinx: ">=1" # The hooks that will be performed when the package is deployed. hooks: From d28904b0af02250f45a61eb1a2361fb62a4475d2 Mon Sep 17 00:00:00 2001 From: GuGuss Date: Tue, 14 Jul 2015 13:42:14 +0200 Subject: [PATCH 0342/2667] Mention Platform.sh on README. --- README.markdown | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.markdown b/README.markdown index 98cc5d386f5..92816a63702 100644 --- a/README.markdown +++ b/README.markdown @@ -14,3 +14,8 @@ Contributing We love contributors! For more information on how you can contribute to the Symfony documentation, please read [Contributing to the Documentation](https://symfony.com/doc/current/contributing/documentation/overview.html) + +Platform.sh +----------- + +Pull requests are automatically built by [Platform.sh](https://platform.sh). From a648d6b108ff39ef6401b4f98db346d6a081225e Mon Sep 17 00:00:00 2001 From: GuGuss Date: Tue, 14 Jul 2015 15:11:42 +0200 Subject: [PATCH 0343/2667] Document Platform.sh on the contributing section. --- contributing/documentation/overview.rst | 22 ++++++++++++++++++ .../docs-pull-request-platformsh.png | Bin 0 -> 93061 bytes 2 files changed, 22 insertions(+) create mode 100644 images/contributing/docs-pull-request-platformsh.png diff --git a/contributing/documentation/overview.rst b/contributing/documentation/overview.rst index 984275d6c61..fd101ae7d08 100644 --- a/contributing/documentation/overview.rst +++ b/contributing/documentation/overview.rst @@ -248,6 +248,27 @@ steps to contribute to the Symfony documentation, which you can use as a You guessed right: after all this hard work, it's **time to celebrate again!** + +Review your changes +------------------- + +Every GitHub Pull Request is automatically built and deployed by `Platform.sh`_ +on a single environment that you can access on your browser to review your +changes. + +.. image:: /images/contributing/docs-pull-request-platformsh.png + :align: center + :alt: Platform.sh Pull Request Deployment + +To access the `Platform.sh`_ environment URL, simply go to your Pull Request +page on GitHub and click on ``Details``. + +.. note:: + + The specific configuration files at the root of the Git repository: + ``.platform.app.yaml``, ``.platform/services.yaml`` and + ``.platform/routes.yaml`` allow `Platform.sh`_ to build Pull Requests. + Minor Changes (e.g. Typos) -------------------------- @@ -324,3 +345,4 @@ definitely don't want you to waste your time! .. _SensioLabsConnect: https://connect.sensiolabs.com/ .. _`Symfony Documentation Badge`: https://connect.sensiolabs.com/badge/36/symfony-documentation-contributor .. _`sync your fork`: https://help.github.com/articles/syncing-a-fork +.. _`Platform.sh`: https://platform.sh \ No newline at end of file diff --git a/images/contributing/docs-pull-request-platformsh.png b/images/contributing/docs-pull-request-platformsh.png new file mode 100644 index 0000000000000000000000000000000000000000..30077cce94fd1a244ed954ce6f4e42544ce751ed GIT binary patch literal 93061 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 z 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 z Date: Wed, 15 Jul 2015 16:38:57 +0200 Subject: [PATCH 0344/2667] TYPO: missing closing parantheses of the array --- cookbook/serializer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/serializer.rst b/cookbook/serializer.rst index 8da5c70a583..8b2d9b296d8 100644 --- a/cookbook/serializer.rst +++ b/cookbook/serializer.rst @@ -169,7 +169,7 @@ to your class and choose which groups to use when serializing:: $serializer = $this->get('serializer'); $json = $serializer->serialize( $someObject, - 'json', array('groups' => array('group1') + 'json', array('groups' => array('group1')) ); .. _cookbook-serializer-enabling-metadata-cache: From 8a0297ffe50c213c50bd4d1ef267765696cc86ad Mon Sep 17 00:00:00 2001 From: snroki Date: Wed, 15 Jul 2015 14:29:33 +0200 Subject: [PATCH 0345/2667] Custom voter example, fix missing curly brace | Q | A | ------------- | --- | Doc fix? | yes | New docs? | no | Applies to | 2.6 / 2.7 / 3.0 | Fixed tickets | --- cookbook/security/voters_data_permission.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index a9d5e2b9076..18ef7df9ef0 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -111,7 +111,8 @@ edit a particular object. Here's an example implementation: } break; - + } + return false; } } From c1e8453636ba1ea65677e6c8b4493a58d7f729a2 Mon Sep 17 00:00:00 2001 From: Alexander Schwenn Date: Wed, 15 Jul 2015 22:43:55 +0200 Subject: [PATCH 0346/2667] [Book][Routing] Change example to match multiple methods --- book/routing.rst | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/book/routing.rst b/book/routing.rst index 14968290474..4476031a7c9 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -833,36 +833,36 @@ be accomplished with the following route configuration: class MainController extends Controller { /** - * @Route("/contact") + * @Route("/news") * @Method("GET") */ - public function contactAction() + public function newsAction() { - // ... display contact form + // ... display your news } /** * @Route("/contact") - * @Method("POST") + * @Method({"GET", "POST"}) */ - public function processContactAction() + public function contactFormAction() { - // ... process contact form + // ... display and process a contact form } } .. code-block:: yaml # app/config/routing.yml - contact: - path: /contact - defaults: { _controller: AppBundle:Main:contact } + news: + path: /news + defaults: { _controller: AppBundle:Main:news } methods: [GET] - contact_process: + contact_form: path: /contact - defaults: { _controller: AppBundle:Main:processContact } - methods: [POST] + defaults: { _controller: AppBundle:Main:contactForm } + methods: [GET, POST] .. code-block:: xml @@ -873,12 +873,12 @@ be accomplished with the following route configuration: xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd"> - - AppBundle:Main:contact + + AppBundle:Main:news - - AppBundle:Main:processContact + + AppBundle:Main:contactForm @@ -889,13 +889,13 @@ be accomplished with the following route configuration: use Symfony\Component\Routing\Route; $collection = new RouteCollection(); - $collection->add('contact', new Route('/contact', array( + $collection->add('news', new Route('/news', array( '_controller' => 'AppBundle:Main:contact', ), array(), array(), '', array(), array('GET'))); - $collection->add('contact_process', new Route('/contact', array( - '_controller' => 'AppBundle:Main:processContact', - ), array(), array(), '', array(), array('POST'))); + $collection->add('contact_form', new Route('/contact', array( + '_controller' => 'AppBundle:Main:contactForm', + ), array(), array(), '', array(), array('GET', 'POST'))); return $collection; From 131ea23fe23ee4c52b699d9823a298be86fd49a5 Mon Sep 17 00:00:00 2001 From: kenjis Date: Tue, 14 Jul 2015 18:11:36 +0900 Subject: [PATCH 0347/2667] Fix RST --- create_framework/routing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_framework/routing.rst b/create_framework/routing.rst index 6bbb46f64b8..c9456b02b04 100644 --- a/create_framework/routing.rst +++ b/create_framework/routing.rst @@ -158,7 +158,7 @@ With this knowledge in mind, let's write the new version of our framework:: $response->send(); -There are a few new things in the code:: +There are a few new things in the code: * Route names are used for template names; From 0a4192cba0135400fad9e761cbd945976cad224b Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 15 Jul 2015 20:32:24 -0400 Subject: [PATCH 0348/2667] [#5444] Fixing missing public: false declarations and proofing --- reference/dic_tags.rst | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 56013b4a52e..f3097e577b5 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -246,10 +246,13 @@ services: services: app.mysql_lock: class: AppBundle\Lock\MysqlLock + public: false app.postgresql_lock: class: AppBundle\Lock\PostgresqlLock + public: false app.sqlite_lock: class: AppBundle\Lock\SqliteLock + public: false .. code-block:: xml @@ -259,26 +262,29 @@ services: xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - + + + .. code-block:: php $container - ->register('app.mysql_lock', 'AppBundle\Lock\MysqlLock') - ->register('app.postgresql_lock', 'AppBundle\Lock\PostgresqlLock') - ->register('app.sqlite_lock', 'AppBundle\Lock\SqliteLock') + ->register('app.mysql_lock', 'AppBundle\Lock\MysqlLock')->setPublic(false) + ->register('app.postgresql_lock', 'AppBundle\Lock\PostgresqlLock')->setPublic(false) + ->register('app.sqlite_lock', 'AppBundle\Lock\SqliteLock')->setPublic(false) ; Instead of dealing with these three services, your application needs a generic -``app.lock`` service. This service must be an alias to any of the other services. -Thanks to the ``auto_alias`` option, you can automatically create that alias -based on the value of a configuration parameter. +``app.lock`` service that will be an alias to one of these services, depending on +some configuration. Thanks to the ``auto_alias`` option, you can automatically create +that alias based on the value of a configuration parameter. -Considering that a configuration parameter called ``database_type`` exists, +Considering that a configuration parameter called ``database_type`` exists. Then, the generic ``app.lock`` service can be defined as follows: .. configuration-block:: @@ -287,14 +293,11 @@ the generic ``app.lock`` service can be defined as follows: services: app.mysql_lock: - class: AppBundle\Lock\MysqlLock - public: false + # ... app.postgresql_lock: - class: AppBundle\Lock\PostgresqlLock - public: false + # ... app.sqlite_lock: - class: AppBundle\Lock\SqliteLock - public: false + # ... app.lock: tags: - { name: auto_alias, format: "app.%database_type%_lock" } @@ -331,8 +334,8 @@ the generic ``app.lock`` service can be defined as follows: ->addTag('auto_alias', array('format' => 'app.%database_type%_lock')) ; -The ``format`` parameter defines the expression used to construct the name of -the service to alias. This expression can use any container parameter (as usual, +The ``format`` option defines the expression used to construct the name of the service +to alias. This expression can use any container parameter (as usual, wrapping their names with ``%`` characters). .. note:: From d4afd3a9cbdace6ce6d1bab5bec1e66768f8c5b7 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 15 Jul 2015 20:49:40 -0400 Subject: [PATCH 0349/2667] [#5453] Minor tweaks - mostly thanks to Javier --- book/security.rst | 2 +- cookbook/security/voters.rst | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/book/security.rst b/book/security.rst index 20d174c314d..9fa34bac766 100644 --- a/book/security.rst +++ b/book/security.rst @@ -929,7 +929,7 @@ other users. Also, as the admin user, you yourself want to be able to edit To accomplish this you have 2 options: -* :doc:`Voters ` allow you to use business logic +* :doc:`Voters ` allow you to write own business logic (e.g. the user can edit this post because they were the creator) to determine access. You'll probably want this option - it's flexible enough to solve the above situation. diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index a665ef0c9f7..2f27e8ff431 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -24,8 +24,9 @@ All voters are called each time you use the ``isGranted()`` method on Symfony's security context (i.e. the ``security.context`` service). Each one decides if the current user should have access to some resource. -Ultimately, Symfony uses one of three different approaches on what to do -with the feedback from all voters: affirmative, consensus and unanimous. +Ultimately, Symfony takes the responses from all voters and makes the final +decission (to allow or deny access to the resource) according to the strategy defined +in the application, which can be: affirmative, consensus or unanimous. For more information take a look at :ref:`the section about access decision managers `. @@ -49,7 +50,7 @@ method is used to check if the voter supports the given user attribute (i.e: a role like ``ROLE_USER``, an ACL ``EDIT``, etc.). The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsClass` -method is used to check if the voter supports the class of the object whose +method checks whether the voter supports the class of the object whose access is being checked. The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::vote` @@ -87,10 +88,7 @@ edit a particular object. Here's an example implementation:: public function supportsAttribute($attribute) { - return in_array($attribute, array( - self::VIEW, - self::EDIT, - )); + return in_array($attribute, array(self::VIEW, self::EDIT)); } public function supportsClass($class) @@ -229,7 +227,7 @@ from the security context is called. // keep in mind, this will call all registered security voters if (false === $this->get('security.context')->isGranted('view', $post)) { - throw new AccessDeniedException('Unauthorised access!'); + throw new AccessDeniedException('Unauthorized access!'); } return new Response('

'.$post->getName().'

'); From 6a34332c20a14b86547e9e7e177c14d0fe7ebed5 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 15 Jul 2015 21:07:31 -0400 Subject: [PATCH 0350/2667] [#5472] Minor tweak and adding code example --- components/security/secure_tools.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/security/secure_tools.rst b/components/security/secure_tools.rst index 924b0795c29..a1279040d24 100644 --- a/components/security/secure_tools.rst +++ b/components/security/secure_tools.rst @@ -50,7 +50,9 @@ to work correctly. Just pass a file name to enable it:: use Symfony\Component\Security\Core\Util\SecureRandom; $generator = new SecureRandom('/some/path/to/store/the/seed.txt'); + $random = $generator->nextBytes(10); + $hashedRandom = md5($random); // see tip below .. note:: @@ -60,7 +62,7 @@ to work correctly. Just pass a file name to enable it:: .. tip:: The ``nextBytes()`` method returns a binary string which may contain the - ``\0`` character. This can cause troubles in lots of common scenarios, such + ``\0`` character. This can cause trouble in several common scenarios, such as storing this value in a data 10000 base or including it as part of the URL. The solution is to hash the value returned by ``nextBytes()`` (to do that, you can use a simple ``md5()`` PHP function). From 1103e00ced05ce83a59c101993c4a3329289678a Mon Sep 17 00:00:00 2001 From: kenjis Date: Thu, 16 Jul 2015 14:25:05 +0900 Subject: [PATCH 0351/2667] Fix mock $matcher --- create_framework/unit-testing.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/create_framework/unit-testing.rst b/create_framework/unit-testing.rst index de26e202570..014e04468b6 100644 --- a/create_framework/unit-testing.rst +++ b/create_framework/unit-testing.rst @@ -157,6 +157,11 @@ Response:: } ))) ; + $matcher + ->expects($this->once()) + ->method('getContext') + ->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext'))) + ; $resolver = new ControllerResolver(); $framework = new Framework($matcher, $resolver); From 02bdd81ee85466602daac96a276abbf3107ceccd Mon Sep 17 00:00:00 2001 From: Philipp Rieber Date: Thu, 16 Jul 2015 09:20:33 +0200 Subject: [PATCH 0352/2667] [Cookbook][upload_file] Fix :methods: to remove doubled braces; +fix asset path; +fix typo --- cookbook/assetic/php.rst | 2 +- cookbook/controller/upload_file.rst | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cookbook/assetic/php.rst b/cookbook/assetic/php.rst index 6a704036ec0..29782359f8c 100644 --- a/cookbook/assetic/php.rst +++ b/cookbook/assetic/php.rst @@ -113,7 +113,7 @@ First, configure a new ``scssphp`` Assetic filter: The value of the ``formatter`` option is the fully qualified class name of the formatter used by the filter to produce the compiled CSS file. Using the -compressed formatter will minimize the the resulting file, regardless of whether +compressed formatter will minimize the resulting file, regardless of whether the original files are regular CSS files or SCSS files. Next, update your Twig template to add the ``{% stylesheets %}`` tag defined diff --git a/cookbook/controller/upload_file.rst b/cookbook/controller/upload_file.rst index d374e1473f0..dccd5676773 100644 --- a/cookbook/controller/upload_file.rst +++ b/cookbook/controller/upload_file.rst @@ -156,14 +156,14 @@ There are some important things to consider in the code of the above controller: provides methods for the most common operations when dealing with uploaded files. #. A well-known security best practice is to never trust the input provided by users. This also applies to the files uploaded by your visitors. The ``Uploaded`` - class provides methods to get the original file extension (:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getExtension()`), - the original file size (:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getSize()`) - and the original file name (:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getClientOriginalName()`). + class provides methods to get the original file extension (:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getExtension`), + the original file size (:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getSize`) + and the original file name (:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getClientOriginalName`). However, they are considered *not safe* because a malicious user could tamper that information. That's why it's always better to generate a unique name and - use the :method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::guessExtension()` + use the :method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::guessExtension` method to let Symfony guess the right extension according to the file MIME type. -#. The ``UploadedFile`` class also provides a :method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::move()` +#. The ``UploadedFile`` class also provides a :method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::move` method to store the file in its intended directory. Defining this directory path as an application configuration option is considered a good practice that simplifies the code: ``$this->container->getParameter('brochures_dir')``. @@ -172,6 +172,6 @@ You can now use the following code to link to the PDF brochure of an product: .. code-block:: html+jinja -
View brochure (PDF) + View brochure (PDF) .. _`VichUploaderBundle`: https://github.com/dustin10/VichUploaderBundle From 3878e1426533d4b479bf02a37e2e43c3cbf09dad Mon Sep 17 00:00:00 2001 From: Willem-Jan Date: Thu, 16 Jul 2015 20:40:13 +0200 Subject: [PATCH 0353/2667] Removed reference to remove HTTPS off from nginx configuration --- cookbook/configuration/web_server_configuration.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/cookbook/configuration/web_server_configuration.rst b/cookbook/configuration/web_server_configuration.rst index 0be18f40d31..2720e8a4f80 100644 --- a/cookbook/configuration/web_server_configuration.rst +++ b/cookbook/configuration/web_server_configuration.rst @@ -269,7 +269,6 @@ The **minimum configuration** to get your application running under Nginx is: fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param HTTPS off; } # PROD location ~ ^/app\.php(/|$) { @@ -277,7 +276,6 @@ The **minimum configuration** to get your application running under Nginx is: fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param HTTPS off; # Prevents URIs that include the front controller. This will 404: # http://domain.tld/app.php/some-path # Remove the internal directive to allow URIs like this From 9099cf296042c4ee4e977ca56531fe858da36100 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 5 Jul 2015 10:48:20 +0200 Subject: [PATCH 0354/2667] review all Security code blocks --- book/security.rst | 69 ++++++--- cookbook/security/access_control.rst | 33 ++-- cookbook/security/acl.rst | 22 ++- cookbook/security/csrf_in_login_form.rst | 36 +++-- .../custom_authentication_provider.rst | 125 ++++++++++----- cookbook/security/custom_provider.rst | 88 ++++++++--- cookbook/security/entity_provider.rst | 71 +++++---- cookbook/security/force_https.rst | 89 ++++++++--- cookbook/security/form_login.rst | 144 +++++++++++++----- cookbook/security/form_login_setup.rst | 69 ++++++--- cookbook/security/impersonating_user.rst | 25 ++- cookbook/security/multiple_user_providers.rst | 1 + cookbook/security/pre_authenticated.rst | 17 ++- cookbook/security/remember_me.rst | 43 +++--- cookbook/security/securing_services.rst | 58 ++++--- cookbook/security/voters.rst | 11 +- 16 files changed, 615 insertions(+), 286 deletions(-) diff --git a/book/security.rst b/book/security.rst index 9fa34bac766..1d1259d5e90 100644 --- a/book/security.rst +++ b/book/security.rst @@ -67,7 +67,7 @@ configuration looks like this: + security="false" /> @@ -81,7 +81,7 @@ configuration looks like this: $container->loadFromExtension('security', array( 'providers' => array( 'in_memory' => array( - 'memory' => array(), + 'memory' => null, ), ), 'firewalls' => array( @@ -209,6 +209,8 @@ user to be logged in to access this URL: # ... firewalls: # ... + default: + # ... access_control: # require ROLE_ADMIN for /admin* @@ -231,10 +233,8 @@ user to be logged in to access this URL: - - - - + + @@ -541,13 +541,14 @@ like this: http://symfony.com/schema/dic/services/services-1.0.xsd"> + + - @@ -555,6 +556,8 @@ like this: // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'providers' => array( 'in_memory' => array( 'memory' => array( @@ -691,8 +694,11 @@ URL pattern. You saw this earlier, where anything matching the regular expressio # app/config/security.yml security: # ... + firewalls: # ... + default: + # ... access_control: # require ROLE_ADMIN for /admin* @@ -715,10 +721,8 @@ URL pattern. You saw this earlier, where anything matching the regular expressio - - - - + + @@ -727,6 +731,7 @@ URL pattern. You saw this earlier, where anything matching the regular expressio // app/config/security.php $container->loadFromExtension('security', array( // ... + 'firewalls' => array( // ... 'default' => array( @@ -755,6 +760,7 @@ matches the URL. # app/config/security.yml security: # ... + access_control: - { path: ^/admin/users, roles: ROLE_SUPER_ADMIN } - { path: ^/admin, roles: ROLE_ADMIN } @@ -771,10 +777,9 @@ matches the URL. - - - - + + + @@ -783,6 +788,7 @@ matches the URL. // app/config/security.php $container->loadFromExtension('security', array( // ... + 'access_control' => array( array('path' => '^/admin/users', 'role' => 'ROLE_SUPER_ADMIN'), array('path' => '^/admin', 'role' => 'ROLE_ADMIN'), @@ -1037,13 +1043,14 @@ the firewall can handle this automatically for you when you activate the # app/config/security.yml security: + # ... + firewalls: secured_area: # ... logout: path: /logout target: / - # ... .. code-block:: xml @@ -1056,11 +1063,12 @@ the firewall can handle this automatically for you when you activate the http://symfony.com/schema/dic/services/services-1.0.xsd"> - + + + - @@ -1068,13 +1076,14 @@ the firewall can handle this automatically for you when you activate the // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( 'secured_area' => array( // ... - 'logout' => array('path' => 'logout', 'target' => '/'), + 'logout' => array('path' => '/logout', 'target' => '/'), ), ), - // ... )); Next, you'll need to create a route for this URL (but not a controller): @@ -1085,7 +1094,7 @@ Next, you'll need to create a route for this URL (but not a controller): # app/config/routing.yml logout: - path: /logout + path: /logout .. code-block:: xml @@ -1106,7 +1115,7 @@ Next, you'll need to create a route for this URL (but not a controller): use Symfony\Component\Routing\Route; $collection = new RouteCollection(); - $collection->add('logout', new Route('/logout', array())); + $collection->add('logout', new Route('/logout')); return $collection; @@ -1171,6 +1180,8 @@ rules by creating a role hierarchy: # app/config/security.yml security: + # ... + role_hierarchy: ROLE_ADMIN: ROLE_USER ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] @@ -1186,6 +1197,8 @@ rules by creating a role hierarchy: http://symfony.com/schema/dic/services/services-1.0.xsd"> + + ROLE_USER ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH @@ -1195,6 +1208,8 @@ rules by creating a role hierarchy: // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'role_hierarchy' => array( 'ROLE_ADMIN' => 'ROLE_USER', 'ROLE_SUPER_ADMIN' => array( @@ -1224,6 +1239,8 @@ cookie will be ever created by Symfony): # app/config/security.yml security: + # ... + firewalls: main: http_basic: ~ @@ -1240,7 +1257,9 @@ cookie will be ever created by Symfony): http://symfony.com/schema/dic/services/services-1.0.xsd"> - + + + @@ -1250,8 +1269,10 @@ cookie will be ever created by Symfony): // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( - 'main' => array('http_basic' => array(), 'stateless' => true), + 'main' => array('http_basic' => null, 'stateless' => true), ), )); diff --git a/cookbook/security/access_control.rst b/cookbook/security/access_control.rst index ec09e05d4b9..1977b4ed403 100644 --- a/cookbook/security/access_control.rst +++ b/cookbook/security/access_control.rst @@ -54,12 +54,10 @@ Take the following ``access_control`` entries as an example: - - - - - - + + + + @@ -82,7 +80,7 @@ Take the following ``access_control`` entries as an example: array( 'path' => '^/admin', 'role' => 'ROLE_USER_METHOD', - 'method' => 'POST, PUT', + 'methods' => 'POST, PUT', ), array( 'path' => '^/admin', @@ -193,11 +191,10 @@ pattern so that it is only accessible by requests from the local server itself: - - - - + + @@ -208,12 +205,12 @@ pattern so that it is only accessible by requests from the local server itself: // ... 'access_control' => array( array( - 'path' => '^/esi', + 'path' => '^/internal', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'ips' => '127.0.0.1, ::1' ), array( - 'path' => '^/esi', + 'path' => '^/internal', 'role' => 'ROLE_NO_ACCESS' ), ), @@ -270,11 +267,9 @@ the user will be redirected to ``https``: xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - + .. code-block:: php diff --git a/cookbook/security/acl.rst b/cookbook/security/acl.rst index 507efe00dd7..0ccb5966b16 100644 --- a/cookbook/security/acl.rst +++ b/cookbook/security/acl.rst @@ -52,20 +52,36 @@ First, you need to configure the connection the ACL system is supposed to use: # app/config/security.yml security: + # ... + acl: connection: default .. code-block:: xml - - default - + + + + + + + + default + + + .. code-block:: php // app/config/security.php $container->loadFromExtension('security', 'acl', array( + // ... + 'connection' => 'default', )); diff --git a/cookbook/security/csrf_in_login_form.rst b/cookbook/security/csrf_in_login_form.rst index d957a2585b5..4db13ced455 100644 --- a/cookbook/security/csrf_in_login_form.rst +++ b/cookbook/security/csrf_in_login_form.rst @@ -26,6 +26,8 @@ provider available in the Form component: # app/config/security.yml security: + # ... + firewalls: secured_area: # ... @@ -35,17 +37,19 @@ provider available in the Form component: .. code-block:: xml - + + xsi:schemaLocation="http://symfony.com/schema/dic/services + http://symfony.com/schema/dic/services/services-1.0.xsd"> + + - @@ -55,15 +59,17 @@ provider available in the Form component: // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( 'secured_area' => array( // ... 'form_login' => array( // ... 'csrf_provider' => 'form.csrf_provider', - ) - ) - ) + ), + ), + ), )); The Security component can be configured further, but this is all information @@ -124,6 +130,8 @@ After this, you have protected your login form against CSRF attacks. # app/config/security.yml security: + # ... + firewalls: secured_area: # ... @@ -134,17 +142,19 @@ After this, you have protected your login form against CSRF attacks. .. code-block:: xml - + + xsi:schemaLocation="http://symfony.com/schema/dic/services + http://symfony.com/schema/dic/services/services-1.0.xsd"> + + - @@ -155,6 +165,8 @@ After this, you have protected your login form against CSRF attacks. // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( 'secured_area' => array( // ... @@ -162,9 +174,9 @@ After this, you have protected your login form against CSRF attacks. // ... 'csrf_parameter' => '_csrf_security_token', 'intention' => 'a_private_string', - ) - ) - ) + ), + ), + ), )); .. _`Cross-site request forgery`: http://en.wikipedia.org/wiki/Cross-site_request_forgery diff --git a/cookbook/security/custom_authentication_provider.rst b/cookbook/security/custom_authentication_provider.rst index fb21870acaf..3f0928903c3 100644 --- a/cookbook/security/custom_authentication_provider.rst +++ b/cookbook/security/custom_authentication_provider.rst @@ -399,19 +399,24 @@ to service ids that do not exist yet: ``wsse.security.authentication.provider`` .. code-block:: yaml - # src/AppBundle/Resources/config/services.yml + # app/config/services.yml services: wsse.security.authentication.provider: class: AppBundle\Security\Authentication\Provider\WsseProvider - arguments: ["", "%kernel.cache_dir%/security/nonces"] + arguments: + - "" # User Provider + - "%kernel.cache_dir%/security/nonces" + public: false wsse.security.authentication.listener: class: AppBundle\Security\Firewall\WsseListener arguments: ["@security.context", "@security.authentication.manager"] + public: false .. code-block:: xml - + + @@ -424,8 +429,10 @@ to service ids that do not exist yet: ``wsse.security.authentication.provider``
- + class="AppBundle\Security\Firewall\WsseListener" + public="false"> + + @@ -433,27 +440,33 @@ to service ids that do not exist yet: ``wsse.security.authentication.provider`` .. code-block:: php - // src/AppBundle/Resources/config/services.php + // app/config/services.php use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - $container->setDefinition('wsse.security.authentication.provider', - new Definition( - 'AppBundle\Security\Authentication\Provider\WsseProvider', array( - '', - '%kernel.cache_dir%/security/nonces', + $container + ->setDefinition('wsse.security.authentication.provider', + new Definition( + 'AppBundle\Security\Authentication\Provider\WsseProvider', array( + '', // User Provider + '%kernel.cache_dir%/security/nonces', + ) ) ) - ); - - $container->setDefinition('wsse.security.authentication.listener', - new Definition( - 'AppBundle\Security\Firewall\WsseListener', array( - new Reference('security.context'), - new Reference('security.authentication.manager'), + ->setPublic(false) + ; + + $container + ->setDefinition('wsse.security.authentication.listener', + new Definition( + 'AppBundle\Security\Firewall\WsseListener', array( + new Reference('security.context'), + new Reference('security.authentication.manager'), + ) ) ) - ); + ->setPublic(false) + ; Now that your services are defined, tell your security context about your factory in your bundle class: @@ -484,30 +497,48 @@ You are finished! You can now define parts of your app as under WSSE protection. .. code-block:: yaml + # app/config/security.yml security: + # ... + firewalls: wsse_secured: - pattern: /api/.* + pattern: ^/api/ stateless: true wsse: true .. code-block:: xml - - - - - - + + + + + + + + + + .. code-block:: php + // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( 'wsse_secured' => array( - 'pattern' => '/api/.*', - 'stateless' => true, - 'wsse' => true, + 'pattern' => '^/api/', + 'stateless' => true, + 'wsse' => true, ), ), )); @@ -587,32 +618,46 @@ set to any desirable value per firewall. .. code-block:: yaml + # app/config/security.yml security: + # ... + firewalls: wsse_secured: - pattern: /api/.* + pattern: ^/api/ stateless: true wsse: { lifetime: 30 } .. code-block:: xml - - - - - - + + + + + + + + + + + + .. code-block:: php + // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( 'wsse_secured' => array( - 'pattern' => '/api/.*', + 'pattern' => '^/api/', 'stateless' => true, - 'wsse' => array( + 'wsse' => array( 'lifetime' => 30, ), ), diff --git a/cookbook/security/custom_provider.rst b/cookbook/security/custom_provider.rst index 5ba3ac7c986..0f4e8d4f9e9 100644 --- a/cookbook/security/custom_provider.rst +++ b/cookbook/security/custom_provider.rst @@ -175,21 +175,29 @@ Now you make the user provider available as a service: .. code-block:: yaml - # src/Acme/WebserviceUserBundle/Resources/config/services.yml + # app/config/services.yml services: webservice_user_provider: class: Acme\WebserviceUserBundle\Security\User\WebserviceUserProvider .. code-block:: xml - - - - + + + + + + + + .. code-block:: php - // src/Acme/WebserviceUserBundle/Resources/config/services.php + // app/config/services.php use Symfony\Component\DependencyInjection\Definition; $container->setDefinition( @@ -221,6 +229,8 @@ to the list of providers in the "security" section. Choose a name for the user p # app/config/security.yml security: + # ... + providers: webservice: id: webservice_user_provider @@ -228,14 +238,26 @@ to the list of providers in the "security" section. Choose a name for the user p .. code-block:: xml - - - + + + + + + + + + .. code-block:: php // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'providers' => array( 'webservice' => array( 'id' => 'webservice_user_provider', @@ -253,20 +275,35 @@ users, e.g. by filling in a login form. You can do this by adding a line to the # app/config/security.yml security: + # ... + encoders: Acme\WebserviceUserBundle\Security\User\WebserviceUser: sha512 .. code-block:: xml - - sha512 - + + + + + + + + + .. code-block:: php // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'encoders' => array( 'Acme\WebserviceUserBundle\Security\User\WebserviceUser' => 'sha512', ), @@ -305,6 +342,8 @@ options, the password may be encoded multiple times and encoded to base64. # app/config/security.yml security: + # ... + encoders: Acme\WebserviceUserBundle\Security\User\WebserviceUser: algorithm: sha512 @@ -314,18 +353,29 @@ options, the password may be encoded multiple times and encoded to base64. .. code-block:: xml - - - + + + + + + + + + .. code-block:: php // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'encoders' => array( 'Acme\WebserviceUserBundle\Security\User\WebserviceUser' => array( 'algorithm' => 'sha512', diff --git a/cookbook/security/entity_provider.rst b/cookbook/security/entity_provider.rst index 6c760ef7dbf..347134229a8 100644 --- a/cookbook/security/entity_provider.rst +++ b/cookbook/security/entity_provider.rst @@ -226,23 +226,31 @@ the username and then check the password (more on passwords in a moment): .. code-block:: xml - - + + - + + - - - + - - - + + + + + - - + + + + + + + .. code-block:: php @@ -253,7 +261,9 @@ the username and then check the password (more on passwords in a moment): 'algorithm' => 'bcrypt', ), ), + // ... + 'providers' => array( 'our_db_provider' => array( 'entity' => array( @@ -264,11 +274,12 @@ the username and then check the password (more on passwords in a moment): ), 'firewalls' => array( 'default' => array( - 'pattern' => '^/', + 'pattern' => '^/', 'http_basic' => null, - 'provider' => 'our_db_provider', + 'provider' => 'our_db_provider', ), ), + // ... )); @@ -487,30 +498,37 @@ To finish this, just remove the ``property`` key from the user provider in # app/config/security.yml security: # ... + providers: our_db_provider: entity: class: AppBundle:User - # ... .. code-block:: xml - - - - - - - - - + + + + + + + + + + + .. code-block:: php // app/config/security.php $container->loadFromExtension('security', array( - ..., + // ... + 'providers' => array( 'our_db_provider' => array( 'entity' => array( @@ -518,7 +536,6 @@ To finish this, just remove the ``property`` key from the user provider in ), ), ), - ..., )); This tells Symfony to *not* query automatically for the User. Instead, when diff --git a/cookbook/security/force_https.rst b/cookbook/security/force_https.rst index 63bb7b2e2b2..e5d38992edb 100644 --- a/cookbook/security/force_https.rst +++ b/cookbook/security/force_https.rst @@ -13,24 +13,44 @@ to use HTTPS then you could use the following configuration: .. code-block:: yaml - access_control: - - { path: ^/secure, roles: ROLE_ADMIN, requires_channel: https } + # app/config/security.yml + security: + # ... + + access_control: + - { path: ^/secure, roles: ROLE_ADMIN, requires_channel: https } .. code-block:: xml - - - + + + + + + + + + + .. code-block:: php - 'access_control' => array( - array( - 'path' => '^/secure', - 'role' => 'ROLE_ADMIN', - 'requires_channel' => 'https', + // app/config/security.php + $container->loadFromExtension('security', array( + // ... + + 'access_control' => array( + array( + 'path' => '^/secure', + 'role' => 'ROLE_ADMIN', + 'requires_channel' => 'https', + ), ), - ), + )); The login form itself needs to allow anonymous access, otherwise users will be unable to authenticate. To force it to use HTTPS you can still use @@ -41,26 +61,47 @@ role: .. code-block:: yaml - access_control: - - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https } + # app/config/security.yml + + security: + # ... + + access_control: + - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https } .. code-block:: xml - - - + + + + + + + + + + .. code-block:: php - 'access_control' => array( - array( - 'path' => '^/login', - 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', - 'requires_channel' => 'https', + // app/config/security.php + $container->loadFromExtension('security', array( + // ... + + 'access_control' => array( + array( + 'path' => '^/login', + 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', + 'requires_channel' => 'https', + ), ), - ), + )); It is also possible to specify using HTTPS in the routing configuration, see :doc:`/cookbook/routing/scheme` for more details. diff --git a/cookbook/security/form_login.rst b/cookbook/security/form_login.rst index 337d02a2fdf..e05e6067e58 100644 --- a/cookbook/security/form_login.rst +++ b/cookbook/security/form_login.rst @@ -52,6 +52,8 @@ if no previous page was stored in the session). To set it to the # app/config/security.yml security: + # ... + firewalls: main: form_login: @@ -61,18 +63,28 @@ if no previous page was stored in the session). To set it to the .. code-block:: xml - - - - - + + + + + + + + + + + .. code-block:: php // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( 'main' => array( // ... @@ -101,6 +113,8 @@ of what URL they had requested previously by setting the # app/config/security.yml security: + # ... + firewalls: main: form_login: @@ -110,18 +124,29 @@ of what URL they had requested previously by setting the .. code-block:: xml - - - - - + + + + + + + + + + + + .. code-block:: php // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( 'main' => array( // ... @@ -147,31 +172,44 @@ this by setting ``use_referer`` to true (it defaults to false): # app/config/security.yml security: + # ... + firewalls: main: + # ... form_login: # ... - use_referer: true + use_referer: true .. code-block:: xml - - - - - + + + + + + + + + + + + .. code-block:: php // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( 'main' => array( // ... - 'form_login' => array( // ... 'use_referer' => true, @@ -238,30 +276,45 @@ option to another value. # app/config/security.yml security: + # ... + firewalls: main: + # ... form_login: target_path_parameter: redirect_url .. code-block:: xml - - - - - + + + + + + + + + + + + .. code-block:: php // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( 'main' => array( + // ... 'form_login' => array( - 'target_path_parameter' => redirect_url, + 'target_path_parameter' => 'redirect_url', ), ), ), @@ -282,8 +335,11 @@ back to the login form itself. You can set this to a different route (e.g. # app/config/security.yml security: + # ... + firewalls: main: + # ... form_login: # ... failure_path: login_failure @@ -291,22 +347,32 @@ back to the login form itself. You can set this to a different route (e.g. .. code-block:: xml - - - - - + + + + + + + + + + + + .. code-block:: php // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( 'main' => array( // ... - 'form_login' => array( // ... 'failure_path' => 'login_failure', diff --git a/cookbook/security/form_login_setup.rst b/cookbook/security/form_login_setup.rst index 5e20bef050d..d5c4ba9922d 100644 --- a/cookbook/security/form_login_setup.rst +++ b/cookbook/security/form_login_setup.rst @@ -45,8 +45,9 @@ First, enable form login under your firewall: http://symfony.com/schema/dic/services/services-1.0.xsd"> - + + @@ -57,8 +58,9 @@ First, enable form login under your firewall: // app/config/security.php $container->loadFromExtension('security', array( 'firewalls' => array( - 'main' => array( - 'anonymous' => array(), + 'default' => array( + 'anonymous' => null, + 'http_basic' => null, 'form_login' => array( 'login_path' => '/login', 'check_path' => '/login_check', @@ -160,7 +162,7 @@ under your ``form_login`` configuration (``/login`` and ``/login_check``): '_controller' => 'AppBundle:Security:login', ))); - $collection->add('login_check', new Route('/login_check', array())); + $collection->add('login_check', new Route('/login_check')); // no controller is bound to this route // as it's handled by the Security system @@ -356,11 +358,18 @@ all URLs (including the ``/login`` URL), will cause a redirect loop: .. code-block:: xml + + - - - - + + + + + .. code-block:: php @@ -388,12 +397,19 @@ fixes the problem: .. code-block:: xml + + - - - - - + + + + + + .. code-block:: php @@ -428,14 +444,23 @@ for the login page: .. code-block:: xml + + - - - - - - - + + + + + + + + + + .. code-block:: php @@ -445,11 +470,11 @@ for the login page: 'firewalls' => array( 'login_firewall' => array( 'pattern' => '^/login$', - 'anonymous' => array(), + 'anonymous' => null, ), 'secured_area' => array( 'pattern' => '^/', - 'form_login' => array(), + 'form_login' => null, ), ), diff --git a/cookbook/security/impersonating_user.rst b/cookbook/security/impersonating_user.rst index dc254f42900..1daba483c36 100644 --- a/cookbook/security/impersonating_user.rst +++ b/cookbook/security/impersonating_user.rst @@ -15,6 +15,8 @@ done by activating the ``switch_user`` firewall listener: # app/config/security.yml security: + # ... + firewalls: main: # ... @@ -29,8 +31,11 @@ done by activating the ``switch_user`` firewall listener: xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> + - + + + @@ -41,10 +46,12 @@ done by activating the ``switch_user`` firewall listener: // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( 'main'=> array( // ... - 'switch_user' => true + 'switch_user' => true, ), ), )); @@ -115,6 +122,8 @@ setting: # app/config/security.yml security: + # ... + firewalls: main: # ... @@ -130,7 +139,9 @@ setting: xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + + + @@ -141,6 +152,8 @@ setting: // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( 'main'=> array( // ... @@ -151,7 +164,7 @@ setting: ), ), )); - + Events ------ @@ -200,13 +213,13 @@ how to change the sticky locale: namespace AppBundle\EventListener; use Symfony\Component\Security\Http\Event\SwitchUserEvent; - + class SwitchUserListener { public function onSwitchUser(SwitchUserEvent $event) { $event->getRequest()->getSession()->set( - '_locale', + '_locale', $event->getTargetUser()->getLocale() ); } diff --git a/cookbook/security/multiple_user_providers.rst b/cookbook/security/multiple_user_providers.rst index 4766ed92e44..3c2f879b5c5 100644 --- a/cookbook/security/multiple_user_providers.rst +++ b/cookbook/security/multiple_user_providers.rst @@ -132,6 +132,7 @@ the first provider is always used: 'provider' => 'user_db', 'http_basic' => array( // ... + 10000 'realm' => 'Secured Demo Area', 'provider' => 'in_memory', ), 'form_login' => array(), diff --git a/cookbook/security/pre_authenticated.rst b/cookbook/security/pre_authenticated.rst index 3b2fb7c2e16..3095f450899 100644 --- a/cookbook/security/pre_authenticated.rst +++ b/cookbook/security/pre_authenticated.rst @@ -26,6 +26,8 @@ Enable the x509 authentication for a particular firewall in the security configu # app/config/security.yml security: + # ... + firewalls: secured_area: pattern: ^/ @@ -34,14 +36,19 @@ Enable the x509 authentication for a particular firewall in the security configu .. code-block:: xml - + + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:srv="http://symfony.com/schema/dic/services" + xsi:schemaLocation="http://symfony.com/schema/dic/services + http://symfony.com/schema/dic/services/services-1.0.xsd"> + + - + @@ -50,9 +57,11 @@ Enable the x509 authentication for a particular firewall in the security configu // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( 'secured_area' => array( - 'pattern' => '^/' + 'pattern' => '^/', 'x509' => array( 'provider' => 'your_user_provider', ), diff --git a/cookbook/security/remember_me.rst b/cookbook/security/remember_me.rst index d405bbf801e..383410653c0 100644 --- a/cookbook/security/remember_me.rst +++ b/cookbook/security/remember_me.rst @@ -15,17 +15,20 @@ the session lasts using a cookie with the ``remember_me`` firewall option: .. code-block:: yaml # app/config/security.yml - firewalls: - default: - # ... - remember_me: - key: "%secret%" - lifetime: 604800 # 1 week in seconds - path: / - # by default, the feature is enabled by checking a - # checkbox in the login form (see below), uncomment the - # below lines to always enable it. - #always_remember_me: true + security: + # ... + + firewalls: + default: + # ... + remember_me: + key: "%secret%" + lifetime: 604800 # 1 week in seconds + path: / + # by default, the feature is enabled by checking a + # checkbox in the login form (see below), uncomment the + # following line to always enable it. + #always_remember_me: true .. code-block:: xml @@ -38,17 +41,19 @@ the session lasts using a cookie with the ``remember_me`` firewall option: http://symfony.com/schema/dic/services/services-1.0.xsd"> + + + + - - path = "/" - /> @@ -57,6 +62,8 @@ the session lasts using a cookie with the ``remember_me`` firewall option: // app/config/security.php $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( 'default' => array( // ... @@ -66,7 +73,7 @@ the session lasts using a cookie with the ``remember_me`` firewall option: 'path' => '/', // by default, the feature is enabled by checking a // checkbox in the login form (see below), uncomment - // the below lines to always enable it. + // the following line to always enable it. //'always_remember_me' => true, ), ), @@ -241,7 +248,7 @@ In the following example, the action is only allowed if the user has the { $isFullyAuthenticated = $this->get('security.context') ->isGranted('IS_AUTHENTICATED_FULLY'); - + if (!$isFullyAuthenticated) { throw new AccessDeniedException(); } diff --git a/cookbook/security/securing_services.rst b/cookbook/security/securing_services.rst index fe92402fb7e..6835ba15f89 100644 --- a/cookbook/security/securing_services.rst +++ b/cookbook/security/securing_services.rst @@ -78,11 +78,18 @@ Then in your service configuration, you can inject the service: .. code-block:: xml - - - - - + + + + + + + + + .. code-block:: php @@ -141,30 +148,32 @@ the :ref:`sidebar ` below): .. code-block:: yaml - # app/services.yml - - # ... + # app/config/services.yml services: newsletter_manager: - # ... + class: AppBundle\Newsletter\NewsletterManager tags: - { name: security.secure_service } .. code-block:: xml - - - - - - - - - + + + + + + + + + + .. code-block:: php - // app/services.php + // app/config/services.php use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; @@ -220,14 +229,14 @@ documentation. .. code-block:: yaml - # app/config/config.yml + # app/config/services.yml jms_security_extra: # ... secure_all_services: true .. code-block:: xml - + - - - + + .. code-block:: php - // app/config/config.php + // app/config/services.php $container->loadFromExtension('jms_security_extra', array( // ... 'secure_all_services' => true, diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index 2f27e8ff431..e4fd59c5a37 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -168,25 +168,28 @@ and tag it with ``security.voter``: .. code-block:: yaml - # src/AppBundle/Resources/config/services.yml + # app/config/services.yml services: security.access.post_voter: class: AppBundle\Security\Authorization\Voter\PostVoter public: false tags: - - { name: security.voter } + - { name: security.voter } .. code-block:: xml - + + + @@ -194,7 +197,7 @@ and tag it with ``security.voter``: .. code-block:: php - // src/AppBundle/Resources/config/services.php + // app/config/services.php $container ->register( 'security.access.post_voter', From 421924bce25d5355b4af75522c83f3cb4c08c854 Mon Sep 17 00:00:00 2001 From: Lars Date: Fri, 17 Jul 2015 11:28:30 +0200 Subject: [PATCH 0355/2667] Update email.rst Fixed unfinished comment block - typo --- cookbook/email/email.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/email/email.rst b/cookbook/email/email.rst index 1b966e94012..361ada048ce 100644 --- a/cookbook/email/email.rst +++ b/cookbook/email/email.rst @@ -130,7 +130,7 @@ template might look something like this: {# app/Resources/views/Emails/registration.html.twig #}

You did it! You registered!

- {# example, assuming you have a route named "login" $} + {# example, assuming you have a route named "login" #} To login, go to: .... Thanks! From 77555a65202817612d696100cc9303b06c0765fc Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 21 Jul 2015 17:00:40 +0200 Subject: [PATCH 0356/2667] [#5486] Even more code block improvements --- cookbook/security/access_control.rst | 7 +- cookbook/security/acl.rst | 4 +- cookbook/security/csrf_in_login_form.rst | 3 +- .../custom_authentication_provider.rst | 49 ++++++----- cookbook/security/custom_provider.rst | 9 +- cookbook/security/entity_provider.rst | 4 +- cookbook/security/force_https.rst | 84 +++++++++---------- cookbook/security/form_login_setup.rst | 1 + cookbook/security/impersonating_user.rst | 20 ++++- cookbook/security/multiple_user_providers.rst | 2 + cookbook/security/remember_me.rst | 6 +- cookbook/security/securing_services.rst | 5 +- cookbook/security/voters.rst | 17 ++-- 13 files changed, 117 insertions(+), 94 deletions(-) diff --git a/cookbook/security/access_control.rst b/cookbook/security/access_control.rst index 1977b4ed403..07ea923651b 100644 --- a/cookbook/security/access_control.rst +++ b/cookbook/security/access_control.rst @@ -193,7 +193,9 @@ pattern so that it is only accessible by requests from the local server itself: + ips="127.0.0.1, ::1" + /> +
@@ -269,7 +271,8 @@ the user will be redirected to ``https``: + requires-channel="https" + /> .. code-block:: php diff --git a/cookbook/security/acl.rst b/cookbook/security/acl.rst index 0ccb5966b16..edb71a4c87b 100644 --- a/cookbook/security/acl.rst +++ b/cookbook/security/acl.rst @@ -70,9 +70,7 @@ First, you need to configure the connection the ACL system is supposed to use: - - default - + diff --git a/cookbook/security/csrf_in_login_form.rst b/cookbook/security/csrf_in_login_form.rst index 4db13ced455..052e8ed44b5 100644 --- a/cookbook/security/csrf_in_login_form.rst +++ b/cookbook/security/csrf_in_login_form.rst @@ -156,7 +156,8 @@ After this, you have protected your login form against CSRF attacks. + intention="a_private_string" + />
diff --git a/cookbook/security/custom_authentication_provider.rst b/cookbook/security/custom_authentication_provider.rst index 3f0928903c3..86324ef7ad8 100644 --- a/cookbook/security/custom_authentication_provider.rst +++ b/cookbook/security/custom_authentication_provider.rst @@ -423,15 +423,17 @@ to service ids that do not exist yet: ``wsse.security.authentication.provider`` + class="AppBundle\Security\Authentication\Provider\WsseProvider" + public="false" + > %kernel.cache_dir%/security/nonces - + public="false" + > @@ -444,29 +446,25 @@ to service ids that do not exist yet: ``wsse.security.authentication.provider`` use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - $container - ->setDefinition('wsse.security.authentication.provider', - new Definition( - 'AppBundle\Security\Authentication\Provider\WsseProvider', array( - '', // User Provider - '%kernel.cache_dir%/security/nonces', - ) - ) + $definition = new Definition( + 'AppBundle\Security\Authentication\Provider\WsseProvider', + array( + '', // User Provider + '%kernel.cache_dir%/security/nonces', ) - ->setPublic(false) - ; - - $container - ->setDefinition('wsse.security.authentication.listener', - new Definition( - 'AppBundle\Security\Firewall\WsseListener', array( - new Reference('security.context'), - new Reference('security.authentication.manager'), - ) - ) + ); + $definition->setPublic(false); + $container->setDefinition('wsse.security.authentication.provider', $definition) + + $definition = new Definition( + 'AppBundle\Security\Firewall\WsseListener', + array( + new Reference('security.context'), + new Reference('security.authentication.manager'), ) - ->setPublic(false) - ; + ); + $definition->setPublic(false); + $container->setDefinition('wsse.security.authentication.listener', $definition); Now that your services are defined, tell your security context about your factory in your bundle class: @@ -524,7 +522,8 @@ You are finished! You can now define parts of your app as under WSSE protection. name="wsse_secured" pattern="^/api/" stateless="true" - wsse="true" /> + wsse="true" + /> diff --git a/cookbook/security/custom_provider.rst b/cookbook/security/custom_provider.rst index 0f4e8d4f9e9..60a53517bbe 100644 --- a/cookbook/security/custom_provider.rst +++ b/cookbook/security/custom_provider.rst @@ -191,7 +191,8 @@ Now you make the user provider available as a service: + class="Acme\WebserviceUserBundle\Security\User\WebserviceUserProvider" + /> @@ -294,7 +295,8 @@ users, e.g. by filling in a login form. You can do this by adding a line to the + algorithm="sha512" + /> @@ -366,7 +368,8 @@ options, the password may be encoded multiple times and encoded to base64. + iterations="1" + /> diff --git a/cookbook/security/entity_provider.rst b/cookbook/security/entity_provider.rst index 347134229a8..c0f128b8097 100644 --- a/cookbook/security/entity_provider.rst +++ b/cookbook/security/entity_provider.rst @@ -239,8 +239,8 @@ the username and then check the password (more on passwords in a moment): - - + diff --git a/cookbook/security/force_https.rst b/cookbook/security/force_https.rst index e5d38992edb..eba38055b34 100644 --- a/cookbook/security/force_https.rst +++ b/cookbook/security/force_https.rst @@ -59,49 +59,49 @@ role: .. configuration-block:: - .. code-block:: yaml - - # app/config/security.yml - - security: - # ... - - access_control: - - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https } - - .. code-block:: xml - - - - - - - - - - - - - .. code-block:: php - - // app/config/security.php - $container->loadFromExtension('security', array( - // ... - - 'access_control' => array( - array( - 'path' => '^/login', - 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', - 'requires_channel' => 'https', - ), + .. code-block:: yaml + + # app/config/security.yml + security: + # ... + + access_control: + - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https } + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + // ... + + 'access_control' => array( + array( + 'path' => '^/login', + 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', + 'requires_channel' => 'https', ), - )); + ), + )); It is also possible to specify using HTTPS in the routing configuration, see :doc:`/cookbook/routing/scheme` for more details. diff --git a/cookbook/security/form_login_setup.rst b/cookbook/security/form_login_setup.rst index d5c4ba9922d..018d3e8dbe2 100644 --- a/cookbook/security/form_login_setup.rst +++ b/cookbook/security/form_login_setup.rst @@ -456,6 +456,7 @@ for the login page: + diff --git a/cookbook/security/impersonating_user.rst b/cookbook/security/impersonating_user.rst index 1daba483c36..c2e91d40007 100644 --- a/cookbook/security/impersonating_user.rst +++ b/cookbook/security/impersonating_user.rst @@ -191,9 +191,23 @@ how to change the sticky locale: .. code-block:: xml - - - + + + + + + + + .. code-block:: php diff --git a/cookbook/security/multiple_user_providers.rst b/cookbook/security/multiple_user_providers.rst index 3c2f879b5c5..14940627057 100644 --- a/cookbook/security/multiple_user_providers.rst +++ b/cookbook/security/multiple_user_providers.rst @@ -41,11 +41,13 @@ a new provider that chains the two together: user_db + + diff --git a/cookbook/security/remember_me.rst b/cookbook/security/remember_me.rst index 383410653c0..f820dfa0dd4 100644 --- a/cookbook/security/remember_me.rst +++ b/cookbook/security/remember_me.rst @@ -48,9 +48,9 @@ the session lasts using a cookie with the ``remember_me`` firewall option: + key="%secret%" + lifetime="604800" + path="/" /> diff --git a/cookbook/security/securing_services.rst b/cookbook/security/securing_services.rst index 6835ba15f89..72feedc49aa 100644 --- a/cookbook/security/securing_services.rst +++ b/cookbook/security/securing_services.rst @@ -86,7 +86,7 @@ Then in your service configuration, you can inject the service: - + @@ -241,7 +241,8 @@ documentation. + xsi:schemaLocation="http://symfony.com/schema/dic/services + http://symfony.com/schema/dic/services/services-1.0.xsd"> diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index e4fd59c5a37..e0d5c3868c2 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -198,14 +198,16 @@ and tag it with ``security.voter``: .. code-block:: php // app/config/services.php - $container - ->register( - 'security.access.post_voter', - 'AppBundle\Security\Authorization\Voter\PostVoter' - ) + use Symfony\Component\DependencyInjection\Definition; + + $definition = new Definition('AppBundle\Security\Authorization\Voter\PostVoter'); + $definition + ->setPublic(false) ->addTag('security.voter') ; + $container->setDefinition('security.access.post_voter', $definition); + How to Use the Voter in a Controller ------------------------------------ @@ -283,10 +285,9 @@ security configuration: xmlns:srv="http://symfony.com/schema/dic/services" 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 - http://symfony.com/schema/dic/security - http://symfony.com/schema/dic/security/security-1.0.xsd" + http://symfony.com/schema/dic/services/services-1.0.xsd" > + From 01cdbf5a6e282433c25c8e7bbe5c996f447c4fcc Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 21 Jul 2015 18:09:49 +0200 Subject: [PATCH 0357/2667] Reviewed some component chapters --- components/class_loader/class_loader.rst | 34 ++-- .../class_loader/class_map_generator.rst | 27 +-- .../class_loader/debug_class_loader.rst | 8 +- components/class_loader/introduction.rst | 8 +- components/class_loader/map_class_loader.rst | 20 +- components/config/caching.rst | 39 ++-- components/config/definition.rst | 111 ++++++----- components/config/introduction.rst | 3 +- components/config/resources.rst | 50 ++--- .../_imports-parameters-note.rst.inc | 6 +- components/dependency_injection/advanced.rst | 16 +- .../dependency_injection/compilation.rst | 181 ++++++++++-------- .../dependency_injection/configurators.rst | 49 ++--- .../dependency_injection/definitions.rst | 16 +- components/dependency_injection/factories.rst | 30 +-- components/dependency_injection/index.rst | 2 +- .../dependency_injection/introduction.rst | 19 +- .../dependency_injection/lazy_services.rst | 25 +-- .../dependency_injection/parameters.rst | 46 ++--- .../dependency_injection/parentservices.rst | 64 ++++--- .../synthetic_services.rst | 12 +- components/dependency_injection/tags.rst | 43 ++--- components/dependency_injection/types.rst | 37 ++-- components/dependency_injection/workflow.rst | 61 +++--- 24 files changed, 479 insertions(+), 428 deletions(-) diff --git a/components/class_loader/class_loader.rst b/components/class_loader/class_loader.rst index c852e91fcb6..26be9882909 100644 --- a/components/class_loader/class_loader.rst +++ b/components/class_loader/class_loader.rst @@ -7,14 +7,14 @@ The PSR-0 Class Loader .. versionadded:: 2.1 The ``ClassLoader`` class was introduced in Symfony 2.1. -If your classes and third-party libraries follow the `PSR-0`_ standard, you -can use the :class:`Symfony\\Component\\ClassLoader\\ClassLoader` class to -load all of your project's classes. +If your classes and third-party libraries follow the `PSR-0`_ standard, +you can use the :class:`Symfony\\Component\\ClassLoader\\ClassLoader` class +to load all of your project's classes. .. tip:: - You can use both the ``ApcClassLoader`` and the ``XcacheClassLoader`` to - :doc:`cache ` a ``ClassLoader`` + You can use both the ``ApcClassLoader`` and the ``XcacheClassLoader`` + to :doc:`cache ` a ``ClassLoader`` instance or the ``DebugClassLoader`` to :doc:`debug ` it. @@ -39,12 +39,12 @@ is straightforward:: .. note:: - The autoloader is automatically registered in a Symfony application (see - ``app/autoload.php``). + The autoloader is automatically registered in a Symfony application + (see ``app/autoload.php``). -Use the :method:`Symfony\\Component\\ClassLoader\\ClassLoader::addPrefix` or -:method:`Symfony\\Component\\ClassLoader\\ClassLoader::addPrefixes` methods to -register your classes:: +Use the :method:`Symfony\\Component\\ClassLoader\\ClassLoader::addPrefix` +or :method:`Symfony\\Component\\ClassLoader\\ClassLoader::addPrefixes` methods +to register your classes:: // register a single namespaces $loader->addPrefix('Symfony', __DIR__.'/vendor/symfony/symfony/src'); @@ -63,9 +63,9 @@ register your classes:: 'Twig_' => __DIR__.'/vendor/twig/twig/lib', )); -Classes from a sub-namespace or a sub-hierarchy of `PEAR`_ classes can be looked -for in a location list to ease the vendoring of a sub-set of classes for large -projects:: +Classes from a sub-namespace or a sub-hierarchy of `PEAR`_ classes can be +looked for in a location list to ease the vendoring of a sub-set of classes +for large projects:: $loader->addPrefixes(array( 'Doctrine\\Common' => __DIR__.'/vendor/doctrine/common/lib', @@ -75,10 +75,10 @@ projects:: )); In this example, if you try to use a class in the ``Doctrine\Common`` namespace -or one of its children, the autoloader will first look for the class under the -``doctrine-common`` directory. If not found, it will then fallback to the default -``Doctrine`` directory (the last one configured) before giving up. The order -of the prefix registrations is significant in this case. +or one of its children, the autoloader will first look for the class under +the ``doctrine-common`` directory. If not found, it will then fallback to +the default ``Doctrine`` directory (the last one configured) before giving +up. The order of the prefix registrations is significant in this case. .. _PEAR: http://pear.php.net/manual/en/standards.naming.php .. _PSR-0: http://www.php-fig.org/psr/psr-0/ diff --git a/components/class_loader/class_map_generator.rst b/components/class_loader/class_map_generator.rst index 772bd9ab693..da1a9c73ab4 100644 --- a/components/class_loader/class_map_generator.rst +++ b/components/class_loader/class_map_generator.rst @@ -5,10 +5,11 @@ The Class Map Generator ======================= -Loading a class usually is an easy task given the `PSR-0`_ and `PSR-4`_ standards. -Thanks to the Symfony ClassLoader component or the autoloading mechanism provided -by Composer, you don't have to map your class names to actual PHP files manually. -Nowadays, PHP libraries usually come with autoloading support through Composer. +Loading a class usually is an easy task given the `PSR-0`_ and `PSR-4`_ +standards. Thanks to the Symfony ClassLoader component or the autoloading +mechanism provided by Composer, you don't have to map your class names to +actual PHP files manually. Nowadays, PHP libraries usually come with autoloading +support through Composer. But from time to time you may have to use a third-party library that comes without any autoloading support and therefore forces you to load each class @@ -44,16 +45,17 @@ it possible to create a map of class names to files. Generating a Class Map ---------------------- -To generate the class map, simply pass the root directory of your class files -to the :method:`Symfony\\Component\\ClassLoader\\ClassMapGenerator::createMap` +To generate the class map, simply pass the root directory of your class +files to the +:method:`Symfony\\Component\\ClassLoader\\ClassMapGenerator::createMap` method:: use Symfony\Component\ClassLoader\ClassMapGenerator; var_dump(ClassMapGenerator::createMap(__DIR__.'/library')); -Given the files and class from the table above, you should see an output like -this: +Given the files and class from the table above, you should see an output +like this: .. code-block:: text @@ -87,8 +89,9 @@ file in the same directory with the following contents:: 'Acme\\Bar' => '/var/www/library/bar/Foo.php', ); -Instead of loading each file manually, you'll only have to register the generated -class map with, for example, the :class:`Symfony\\Component\\ClassLoader\\MapClassLoader`:: +Instead of loading each file manually, you'll only have to register the +generated class map with, for example, the +:class:`Symfony\\Component\\ClassLoader\\MapClassLoader`:: use Symfony\Component\ClassLoader\MapClassLoader; @@ -110,8 +113,8 @@ class map with, for example, the :class:`Symfony\\Component\\ClassLoader\\MapCla component. Besides dumping the class map for one directory, you can also pass an array -of directories for which to generate the class map (the result actually is -the same as in the example above):: +of directories for which to generate the class map (the result actually +is the same as in the example above):: use Symfony\Component\ClassLoader\ClassMapGenerator; diff --git a/components/class_loader/debug_class_loader.rst b/components/class_loader/debug_class_loader.rst index cc99762b5cc..1f08128749f 100644 --- a/components/class_loader/debug_class_loader.rst +++ b/components/class_loader/debug_class_loader.rst @@ -7,10 +7,10 @@ Debugging a Class Loader .. versionadded:: 2.1 The ``DebugClassLoader`` class was introduced in Symfony 2.1. -The :class:`Symfony\\Component\\ClassLoader\\DebugClassLoader` attempts to -throw more helpful exceptions when a class isn't found by the registered -autoloaders. All autoloaders that implement a ``findFile()`` method are replaced -with a ``DebugClassLoader`` wrapper. +The :class:`Symfony\\Component\\ClassLoader\\DebugClassLoader` attempts +to throw more helpful exceptions when a class isn't found by the registered +autoloaders. All autoloaders that implement a ``findFile()`` method are +replaced with a ``DebugClassLoader`` wrapper. Using the ``DebugClassLoader`` is as easy as calling its static :method:`Symfony\\Component\\ClassLoader\\DebugClassLoader::enable` method:: diff --git a/components/class_loader/introduction.rst b/components/class_loader/introduction.rst index 9ba98db987d..6bc7a6c70c6 100644 --- a/components/class_loader/introduction.rst +++ b/components/class_loader/introduction.rst @@ -11,11 +11,12 @@ Usage ----- Whenever you reference a class that has not been required or included yet, -PHP uses the `autoloading mechanism`_ to delegate the loading of a file defining -the class. Symfony provides two autoloaders, which are able to load your classes: +PHP uses the `autoloading mechanism`_ to delegate the loading of a file +defining the class. Symfony provides two autoloaders, which are able to +load your classes: * :doc:`/components/class_loader/class_loader`: loads classes that follow - the `PSR-0` class naming standard; + the `PSR-0`_ class naming standard; * :doc:`/components/class_loader/map_class_loader`: loads classes using a static map from class name to file path. @@ -38,5 +39,6 @@ You can install the component in 2 different ways: .. include:: /components/require_autoload.rst.inc +.. _PSR-0: http://www.php-fig.org/psr/psr-0/ .. _`autoloading mechanism`: http://php.net/manual/en/language.oop5.autoload.php .. _Packagist: https://packagist.org/packages/symfony/class-loader diff --git a/components/class_loader/map_class_loader.rst b/components/class_loader/map_class_loader.rst index 2705fa24282..f9bd70abfa4 100644 --- a/components/class_loader/map_class_loader.rst +++ b/components/class_loader/map_class_loader.rst @@ -4,20 +4,22 @@ MapClassLoader ============== -The :class:`Symfony\\Component\\ClassLoader\\MapClassLoader` allows you to -autoload files via a static map from classes to files. This is useful if you -use third-party libraries which don't follow the `PSR-0`_ standards and so -can't use the :doc:`PSR-0 class loader `. +The :class:`Symfony\\Component\\ClassLoader\\MapClassLoader` allows you +to autoload files via a static map from classes to files. This is useful +if you use third-party libraries which don't follow the `PSR-0`_ standards +and so can't use the +:doc:`PSR-0 class loader `. -The ``MapClassLoader`` can be used along with the :doc:`PSR-0 class loader ` -by configuring and calling the ``register()`` method on both. +The ``MapClassLoader`` can be used along with the +:doc:`PSR-0 class loader ` by +configuring and calling the ``register()`` method on both. .. note:: The default behavior is to append the ``MapClassLoader`` on the autoload - stack. If you want to use it as the first autoloader, pass ``true`` when - calling the ``register()`` method. Your class loader will then be prepended - on the autoload stack. + stack. If you want to use it as the first autoloader, pass ``true`` + when calling the ``register()`` method. Your class loader will then + be prepended on the autoload stack. Usage ----- diff --git a/components/config/caching.rst b/components/config/caching.rst index 4bb43558c64..4984a59d755 100644 --- a/components/config/caching.rst +++ b/components/config/caching.rst @@ -1,26 +1,27 @@ .. index:: single: Config; Caching based on resources -Caching Based on Resources +Caching based on Resources ========================== -When all configuration resources are loaded, you may want to process the configuration -values and combine them all in one file. This file acts like a cache. Its -contents don’t have to be regenerated every time the application runs – only -when the configuration resources are modified. +When all configuration resources are loaded, you may want to process the +configuration values and combine them all in one file. This file acts +like a cache. Its contents don’t have to be regenerated every time the +application runs – only when the configuration resources are modified. For example, the Symfony Routing component allows you to load all routes, and then dump a URL matcher or a URL generator based on these routes. In -this case, when one of the resources is modified (and you are working in a -development environment), the generated file should be invalidated and regenerated. -This can be accomplished by making use of the :class:`Symfony\\Component\\Config\\ConfigCache` -class. - -The example below shows you how to collect resources, then generate some code -based on the resources that were loaded, and write this code to the cache. The -cache also receives the collection of resources that were used for generating -the code. By looking at the "last modified" timestamp of these resources, -the cache can tell if it is still fresh or that its contents should be regenerated:: +this case, when one of the resources is modified (and you are working +in a development environment), the generated file should be invalidated +and regenerated. This can be accomplished by making use of the +:class:`Symfony\\Component\\Config\\ConfigCache` class. + +The example below shows you how to collect resources, then generate some +code based on the resources that were loaded and write this code to the +cache. The cache also receives the collection of resources that were used +for generating the code. By looking at the "last modified" timestamp of +these resources, the cache can tell if it is still fresh or that its contents +should be regenerated:: use Symfony\Component\Config\ConfigCache; use Symfony\Component\Config\Resource\FileResource; @@ -52,8 +53,8 @@ the cache can tell if it is still fresh or that its contents should be regenerat // you may want to require the cached code: require $cachePath; -In debug mode, a ``.meta`` file will be created in the same directory as the -cache file itself. This ``.meta`` file contains the serialized resources, -whose timestamps are used to determine if the cache is still fresh. When not -in debug mode, the cache is considered to be "fresh" as soon as it exists, +In debug mode, a ``.meta`` file will be created in the same directory as +the cache file itself. This ``.meta`` file contains the serialized resources, +whose timestamps are used to determine if the cache is still fresh. When +not in debug mode, the cache is considered to be "fresh" as soon as it exists, and therefore no ``.meta`` file will be generated. diff --git a/components/config/definition.rst b/components/config/definition.rst index b79ad3853a2..b13be69b8b1 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -8,12 +8,13 @@ Validating Configuration Values ------------------------------- After loading configuration values from all kinds of resources, the values -and their structure can be validated using the "Definition" part of the Config -Component. Configuration values are usually expected to show some kind of -hierarchy. Also, values should be of a certain type, be restricted in number -or be one of a given set of values. For example, the following configuration -(in YAML) shows a clear hierarchy and some validation rules that should be -applied to it (like: "the value for ``auto_connect`` must be a boolean value"): +and their structure can be validated using the "Definition" part of the +Config Component. Configuration values are usually expected to show some +kind of hierarchy. Also, values should be of a certain type, be restricted +in number or be one of a given set of values. For example, the following +configuration (in YAML) shows a clear hierarchy and some validation rules +that should be applied to it (like: "the value for ``auto_connect`` must +be a boolean value"): .. code-block:: yaml @@ -44,9 +45,9 @@ Defining a Hierarchy of Configuration Values Using the TreeBuilder All the rules concerning configuration values can be defined using the :class:`Symfony\\Component\\Config\\Definition\\Builder\\TreeBuilder`. -A :class:`Symfony\\Component\\Config\\Definition\\Builder\\TreeBuilder` instance -should be returned from a custom ``Configuration`` class which implements the -:class:`Symfony\\Component\\Config\\Definition\\ConfigurationInterface`:: +A :class:`Symfony\\Component\\Config\\Definition\\Builder\\TreeBuilder` +instance should be returned from a custom ``Configuration`` class which +implements the :class:`Symfony\\Component\\Config\\Definition\\ConfigurationInterface`:: namespace Acme\DatabaseConfiguration; @@ -89,7 +90,8 @@ reflect the real structure of the configuration values:: The root node itself is an array node, and has children, like the boolean node ``auto_connect`` and the scalar node ``default_connection``. In general: -after defining a node, a call to ``end()`` takes you one step up in the hierarchy. +after defining a node, a call to ``end()`` takes you one step up in the +hierarchy. Node Type ~~~~~~~~~ @@ -97,13 +99,15 @@ Node Type It is possible to validate the type of a provided value by using the appropriate node definition. Node types are available for: -* scalar (generic type that includes booleans, strings, integers, floats and ``null``) +* scalar (generic type that includes booleans, strings, integers, floats + and ``null``) * boolean * scalar * boolean * integer (new in 2.2) * float (new in 2.2) -* enum (new in 2.1) (similar to scalar, but it only allows a finite set of values) +* enum (new in 2.1) (similar to scalar, but it only allows a finite set + of values) * array * variable (no validation) @@ -201,12 +205,14 @@ Array Node Options Before defining the children of an array node, you can provide options like: ``useAttributeAsKey()`` - Provide the name of a child node, whose value should be used as the key in the resulting array. + Provide the name of a child node, whose value should be used as the + key in the resulting array. ``requiresAtLeastOneElement()`` - There should be at least one element in the array (works only when ``isRequired()`` is also - called). + There should be at least one element in the array (works only when + ``isRequired()`` is also called). ``addDefaultsIfNotSet()`` - If any child nodes have default values, use them if explicit values haven't been provided. + If any child nodes have default values, use them if explicit values + haven't been provided. An example of this:: @@ -233,12 +239,12 @@ In YAML, the configuration might look like this: parameters: param1: { value: param1val } -In XML, each ``parameters`` node would have a ``name`` attribute (along with -``value``), which would be removed and used as the key for that element in -the final array. The ``useAttributeAsKey`` is useful for normalizing how -arrays are specified between different formats like XML and YAML. +In XML, each ``parameters`` node would have a ``name`` attribute (along +with ``value``), which would be removed and used as the key for that element +in the final array. The ``useAttributeAsKey`` is useful for normalizing +how arrays are specified between different formats like XML and YAML. -Default and required Values +Default and Required Values --------------------------- For all node types, it is possible to define default values and replacement @@ -254,7 +260,8 @@ has a certain value: ``default*()`` (``null``, ``true``, ``false``), shortcut for ``defaultValue()`` ``treat*Like()`` - (``null``, ``true``, ``false``), provide a replacement value in case the value is ``*.`` + (``null``, ``true``, ``false``), provide a replacement value in case + the value is ``*.`` .. code-block:: php @@ -296,8 +303,8 @@ All options can be documented using the :method:`Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::info` method. -The info will be printed as a comment when dumping the configuration tree with -the ``config:dump`` command. +The info will be printed as a comment when dumping the configuration tree +with the ``config:dump`` command. Optional Sections ----------------- @@ -308,8 +315,10 @@ Optional Sections If you have entire sections which are optional and can be enabled/disabled, you can take advantage of the shortcut -:method:`Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition::canBeEnabled` and -:method:`Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition::canBeDisabled` methods:: +:method:`Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition::canBeEnabled` +and +:method:`Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition::canBeDisabled` +methods:: $arrayNode ->canBeEnabled() @@ -335,21 +344,22 @@ Merging Options Extra options concerning the merge process may be provided. For arrays: ``performNoDeepMerging()`` - When the value is also defined in a second configuration array, don’t + When the value is also defined in a second configuration array, don't try to merge an array, but overwrite it entirely For all nodes: ``cannotBeOverwritten()`` - don’t let other configuration arrays overwrite an existing value for this node + don't let other configuration arrays overwrite an existing value for + this node Appending Sections ------------------ If you have a complex configuration to validate then the tree can grow to -be large and you may want to split it up into sections. You can do this by -making a section a separate node and then appending it into the main tree -with ``append()``:: +be large and you may want to split it up into sections. You can do this +by making a section a separate node and then appending it into the main +tree with ``append()``:: public function getConfigTreeBuilder() { @@ -413,9 +423,9 @@ and finally the tree is used to validate the resulting array. The normalization process is used to remove some of the differences that result from different configuration formats, mainly the differences between YAML and XML. -The separator used in keys is typically ``_`` in YAML and ``-`` in XML. For -example, ``auto_connect`` in YAML and ``auto-connect`` in XML. -The normalization would make both of these ``auto_connect``. +The separator used in keys is typically ``_`` in YAML and ``-`` in XML. +For example, ``auto_connect`` in YAML and ``auto-connect`` in XML. The +normalization would make both of these ``auto_connect``. .. caution:: @@ -440,8 +450,8 @@ and in XML: This difference can be removed in normalization by pluralizing the key used -in XML. You can specify that you want a key to be pluralized in this way with -``fixXmlConfig()``:: +in XML. You can specify that you want a key to be pluralized in this way +with ``fixXmlConfig()``:: $rootNode ->fixXmlConfig('extension') @@ -483,9 +493,9 @@ in the second making it difficult to validate. You can ensure it is always an array with ``fixXmlConfig``. You can further control the normalization process if you need to. For example, -you may want to allow a string to be set and used as a particular key or several -keys to be set explicitly. So that, if everything apart from ``name`` is optional -in this config: +you may want to allow a string to be set and used as a particular key or +several keys to be set explicitly. So that, if everything apart from ``name`` +is optional in this config: .. code-block:: yaml @@ -543,8 +553,8 @@ The builder is used for adding advanced validation rules to node definitions, li ->end() ; -A validation rule always has an "if" part. You can specify this part in the -following ways: +A validation rule always has an "if" part. You can specify this part in +the following ways: - ``ifTrue()`` - ``ifString()`` @@ -568,19 +578,24 @@ of the node's original value. Processing Configuration Values ------------------------------- -The :class:`Symfony\\Component\\Config\\Definition\\Processor` uses the tree -as it was built using the :class:`Symfony\\Component\\Config\\Definition\\Builder\\TreeBuilder` -to process multiple arrays of configuration values that should be merged. -If any value is not of the expected type, is mandatory and yet undefined, -or could not be validated in some other way, an exception will be thrown. +The :class:`Symfony\\Component\\Config\\Definition\\Processor` uses the +tree as it was built using the +:class:`Symfony\\Component\\Config\\Definition\\Builder\\TreeBuilder` to +process multiple arrays of configuration values that should be merged. If +any value is not of the expected type, is mandatory and yet undefined, or +could not be validated in some other way, an exception will be thrown. Otherwise the result is a clean array of configuration values:: use Symfony\Component\Yaml\Yaml; use Symfony\Component\Config\Definition\Processor; use Acme\DatabaseConfiguration; - $config1 = Yaml::parse(file_get_contents(__DIR__.'/src/Matthias/config/config.yml')); - $config2 = Yaml::parse(file_get_contents(__DIR__.'/src/Matthias/config/config_extra.yml')); + $config1 = Yaml::parse( + file_get_contents(__DIR__.'/src/Matthias/config/config.yml') + ); + $config2 = Yaml::parse( + file_get_contents(__DIR__.'/src/Matthias/config/config_extra.yml') + ); $configs = array($config1, $config2); diff --git a/components/config/introduction.rst b/components/config/introduction.rst index 70f74c2072f..154cca424b8 100644 --- a/components/config/introduction.rst +++ b/components/config/introduction.rst @@ -14,7 +14,8 @@ Installation You can install the component in 2 different ways: -* :doc:`Install it via Composer ` (``symfony/config`` on `Packagist`_); +* :doc:`Install it via Composer ` (``symfony/config`` + on `Packagist`_); * Use the official Git repository (https://github.com/symfony/Config). .. include:: /components/require_autoload.rst.inc diff --git a/components/config/resources.rst b/components/config/resources.rst index 1a5fba23c2b..a7905e0747d 100644 --- a/components/config/resources.rst +++ b/components/config/resources.rst @@ -14,8 +14,9 @@ Loading Resources Locating Resources ------------------ -Loading the configuration normally starts with a search for resources – in -most cases: files. This can be done with the :class:`Symfony\\Component\\Config\\FileLocator`:: +Loading the configuration normally starts with a search for resources – +in most cases: files. This can be done with the +:class:`Symfony\\Component\\Config\\FileLocator`:: use Symfony\Component\Config\FileLocator; @@ -24,20 +25,21 @@ most cases: files. This can be done with the :class:`Symfony\\Component\\Config\ $locator = new FileLocator($configDirectories); $yamlUserFiles = $locator->locate('users.yml', null, false); -The locator receives a collection of locations where it should look for files. -The first argument of ``locate()`` is the name of the file to look for. The -second argument may be the current path and when supplied, the locator will -look in this directory first. The third argument indicates whether or not the -locator should return the first file it has found, or an array containing -all matches. +The locator receives a collection of locations where it should look for +files. The first argument of ``locate()`` is the name of the file to look +for. The second argument may be the current path and when supplied, the +locator will look in this directory first. The third argument indicates +whether or not the locator should return the first file it has found or +an array containing all matches. Resource Loaders ---------------- -For each type of resource (YAML, XML, annotation, etc.) a loader must be defined. -Each loader should implement :class:`Symfony\\Component\\Config\\Loader\\LoaderInterface` -or extend the abstract :class:`Symfony\\Component\\Config\\Loader\\FileLoader` -class, which allows for recursively importing other resources:: +For each type of resource (YAML, XML, annotation, etc.) a loader must be +defined. Each loader should implement +:class:`Symfony\\Component\\Config\\Loader\\LoaderInterface` or extend the +abstract :class:`Symfony\\Component\\Config\\Loader\\FileLoader` class, +which allows for recursively importing other resources:: use Symfony\Component\Config\Loader\FileLoader; use Symfony\Component\Yaml\Yaml; @@ -64,19 +66,21 @@ class, which allows for recursively importing other resources:: } } -Finding the right Loader +Finding the Right Loader ------------------------ -The :class:`Symfony\\Component\\Config\\Loader\\LoaderResolver` receives as -its first constructor argument a collection of loaders. When a resource (for -instance an XML file) should be loaded, it loops through this collection -of loaders and returns the loader which supports this particular resource type. - -The :class:`Symfony\\Component\\Config\\Loader\\DelegatingLoader` makes use -of the :class:`Symfony\\Component\\Config\\Loader\\LoaderResolver`. When -it is asked to load a resource, it delegates this question to the -:class:`Symfony\\Component\\Config\\Loader\\LoaderResolver`. In case the resolver -has found a suitable loader, this loader will be asked to load the resource:: +The :class:`Symfony\\Component\\Config\\Loader\\LoaderResolver` receives +as its first constructor argument a collection of loaders. When a resource +(for instance an XML file) should be loaded, it loops through this collection +of loaders and returns the loader which supports this particular resource +type. + +The :class:`Symfony\\Component\\Config\\Loader\\DelegatingLoader` makes +use of the :class:`Symfony\\Component\\Config\\Loader\\LoaderResolver`. +When it is asked to load a resource, it delegates this question to the +:class:`Symfony\\Component\\Config\\Loader\\LoaderResolver`. In case the +resolver has found a suitable loader, this loader will be asked to load +the resource:: use Symfony\Component\Config\Loader\LoaderResolver; use Symfony\Component\Config\Loader\DelegatingLoader; diff --git a/components/dependency_injection/_imports-parameters-note.rst.inc b/components/dependency_injection/_imports-parameters-note.rst.inc index 2f35ec5bdef..4fd8b4005e0 100644 --- a/components/dependency_injection/_imports-parameters-note.rst.inc +++ b/components/dependency_injection/_imports-parameters-note.rst.inc @@ -1,8 +1,8 @@ .. note:: - Due to the way in which parameters are resolved, you cannot use them to - build paths in imports dynamically. This means that something like the - following doesn't work: + Due to the way in which parameters are resolved, you cannot use them + to build paths in imports dynamically. This means that something like + the following doesn't work: .. configuration-block:: diff --git a/components/dependency_injection/advanced.rst b/components/dependency_injection/advanced.rst index 5e9b09d5ae0..93372c8723e 100644 --- a/components/dependency_injection/advanced.rst +++ b/components/dependency_injection/advanced.rst @@ -1,19 +1,19 @@ .. index:: - single: DependencyInjection; Advanced configuration + single: DependencyInjection; Advanced configuration Advanced Container Configuration ================================ .. _container-private-services: -Marking Services as public / private +Marking Services as Public / Private ------------------------------------ When defining services, you'll usually want to be able to access these definitions -within your application code. These services are called ``public``. For example, -the ``doctrine`` service registered with the container when using the DoctrineBundle -is a public service. This means that you can fetch it from the container -using the ``get()`` method:: +within your application code. These services are called ``public``. For +example, the ``doctrine`` service registered with the container when using +the DoctrineBundle is a public service. This means that you can fetch it +from the container using the ``get()`` method:: $doctrine = $container->get('doctrine'); @@ -68,8 +68,8 @@ This *may or may not work*, depending on if the service could be inlined. Simply said: A service can be marked as private if you do not want to access it directly from your code. -However, if a service has been marked as private, you can still alias it (see -below) to access this service (via the alias). +However, if a service has been marked as private, you can still alias it +(see below) to access this service (via the alias). .. note:: diff --git a/components/dependency_injection/compilation.rst b/components/dependency_injection/compilation.rst index e9b9939bff8..34552dafe97 100644 --- a/components/dependency_injection/compilation.rst +++ b/components/dependency_injection/compilation.rst @@ -1,5 +1,5 @@ .. index:: - single: DependencyInjection; Compilation + single: DependencyInjection; Compilation Compiling the Container ======================= @@ -8,8 +8,8 @@ The service container can be compiled for various reasons. These reasons include checking for any potential issues such as circular references and making the container more efficient by resolving parameters and removing unused services. Also, certain features - like using -:doc:`parent servi 10000 ces ` - -require the container to be compiled. +:doc:`parent services ` +- require the container to be compiled. It is compiled by running:: @@ -17,12 +17,13 @@ It is compiled by running:: The compile method uses *Compiler Passes* for the compilation. The DependencyInjection component comes with several passes which are automatically registered for -compilation. For example the :class:`Symfony\\Component\\DependencyInjection\\Compiler\\CheckDefinitionValidityPass` -checks for various potential issues with the definitions that have been set -in the container. After this and several other passes that check the container's -validity, further compiler passes are used to optimize the configuration -before it is cached. For example, private services and abstract services -are removed, and aliases are resolved. +compilation. For example the +:class:`Symfony\\Component\\DependencyInjection\\Compiler\\CheckDefinitionValidityPass` +checks for various potential issues with the definitions that have been +set in the container. After this and several other passes that check the +container's validity, further compiler passes are used to optimize the +configuration before it is cached. For example, private services and abstract +services are removed and aliases are resolved. .. _components-dependency-injection-extension: @@ -30,27 +31,29 @@ Managing Configuration with Extensions -------------------------------------- As well as loading configuration directly into the container as shown in -:doc:`/components/dependency_injection/introduction`, you can manage it by -registering extensions with the container. The first step in the compilation +:doc:`/components/dependency_injection/introduction`, you can manage it +by registering extensions with the container. The first step in the compilation process is to load configuration from any extension classes registered with the container. Unlike the configuration loaded directly, they are only processed when the container is compiled. If your application is modular then extensions allow each module to register and manage their own service configuration. -The extensions must implement :class:`Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface` +The extensions must implement +:class:`Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface` and can be registered with the container with:: $container->registerExtension($extension); -The main work of the extension is done in the ``load`` method. In the ``load`` method -you can load configuration from one or more configuration files as well as -manipulate the container definitions using the methods shown in :doc:`/components/dependency_injection/definitions`. +The main work of the extension is done in the ``load`` method. In the ``load`` +method you can load configuration from one or more configuration files as +well as manipulate the container definitions using the methods shown in +:doc:`/components/dependency_injection/definitions`. The ``load`` method is passed a fresh container to set up, which is then -merged afterwards into the container it is registered with. This allows you -to have several extensions managing container definitions independently. -The extensions do not add to the containers configuration when they are added -but are processed when the container's ``compile`` method is called. +merged afterwards into the container it is registered with. This allows +you to have several extensions managing container definitions independently. +The extensions do not add to the containers configuration when they are +added but are processed when the container's ``compile`` method is called. A very simple extension may just load configuration files into the container:: @@ -73,14 +76,14 @@ A very simple extension may just load configuration files into the container:: // ... } -This does not gain very much compared to loading the file directly into the -overall container being built. It just allows the files to be split up amongst -the modules/bundles. Being able to affect the configuration of a module from -configuration files outside of the module/bundle is needed to make a complex -application configurable. This can be done by specifying sections of config files -loaded directly into the container as being for a particular extension. These -sections on the config will not be processed directly by the container but by the -relevant Extension. +This does not gain very much compared to loading the file directly into +the overall container being built. It just allows the files to be split +up amongst the modules/bundles. Being able to affect the configuration +of a module from configuration files outside of the module/bundle is needed +to make a complex application configurable. This can be done by specifying +sections of config files loaded directly into the container as being for +a particular extension. These sections on the config will not be processed +directly by the container but by the relevant Extension. The Extension must specify a ``getAlias`` method to implement the interface:: @@ -96,8 +99,8 @@ The Extension must specify a ``getAlias`` method to implement the interface:: } } -For YAML configuration files specifying the alias for the Extension as a key -will mean that those values are passed to the Extension's ``load`` method: +For YAML configuration files specifying the alias for the Extension as a +key will mean that those values are passed to the Extension's ``load`` method: .. code-block:: yaml @@ -106,8 +109,9 @@ will mean that those values are passed to the Extension's ``load`` method: foo: fooValue bar: barValue -If this file is loaded into the configuration then the values in it are only -processed when the container is compiled at which point the Extensions are loaded:: +If this file is loaded into the configuration then the values in it are +only processed when the container is compiled at which point the Extensions +are loaded:: use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; @@ -138,9 +142,9 @@ argument of the ``load`` method of the extension:: } The ``$configs`` argument is an array containing each different config file -that was loaded into the container. You are only loading a single config file -in the above example but it will still be within an array. The array will look -like this:: +that was loaded into the container. You are only loading a single config +file in the above example but it will still be within an array. The array +will look like this:: array( array( @@ -150,9 +154,9 @@ like this:: ) Whilst you can manually manage merging the different files, it is much better -to use :doc:`the Config component ` to merge -and validate the config values. Using the configuration processing you could -access the config value this way:: +to use :doc:`the Config component ` to +merge and validate the config values. Using the configuration processing +you could access the config value this way:: use Symfony\Component\Config\Definition\Processor; // ... @@ -207,13 +211,13 @@ The XML version of the config would then look like this: .. note:: - In the Symfony full-stack Framework there is a base Extension class which - implements these methods as well as a shortcut method for processing the - configuration. See :doc:`/cookbook/bundles/extension` for more details. + In the Symfony full-stack Framework there is a base Extension class + which implements these methods as well as a shortcut method for processing + the configuration. See :doc:`/cookbook/bundles/extension` for more details. -The processed config value can now be added as container parameters as if it were -listed in a ``parameters`` section of the config file but with the additional -benefit of merging multiple files and validation of the configuration:: +The processed config value can now be added as container parameters as if +it were listed in a ``parameters`` section of the config file but with the +additional benefit of merging multiple files and validation of the configuration:: public function load(array $configs, ContainerBuilder $container) { @@ -227,8 +231,8 @@ benefit of merging multiple files and validation of the configuration:: } More complex configuration requirements can be catered for in the Extension -classes. For example, you may choose to load a main service configuration file -but also load a secondary one only if a certain parameter is set:: +classes. For example, you may choose to load a main service configuration +file but also load a secondary one only if a certain parameter is set:: public function load(array $configs, ContainerBuilder $container) { @@ -278,11 +282,12 @@ Prepending Configuration Passed to the Extension ------------------------------------------------ .. versionadded:: 2.2 - The ability to prepend the configuration of a bundle was introduced in - Symfony 2.2. + The ability to prepend the configuration of a bundle was introduced + in Symfony 2.2. An Extension can prepend the configuration of any Bundle before the ``load()`` -method is called by implementing :class:`Symfony\\Component\\DependencyInjection\\Extension\\PrependExtensionInterface`:: +method is called by implementing +:class:`Symfony\\Component\\DependencyInjection\\Extension\\PrependExtensionInterface`:: use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; // ... @@ -301,8 +306,9 @@ method is called by implementing :class:`Symfony\\Component\\DependencyInjection } } -For more details, see :doc:`/cookbook/bundles/prepend_extension`, which is -specific to the Symfony Framework, but contains more details about this feature. +For more details, see :doc:`/cookbook/bundles/prepend_extension`, which +is specific to the Symfony Framework, but contains more details about this +feature. Creating a Compiler Pass ------------------------ @@ -310,9 +316,9 @@ Creating a Compiler Pass You can also create and register your own compiler passes with the container. To create a compiler pass it needs to implement the :class:`Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface` -interface. The compiler pass gives you an opportunity to manipulate the service -definitions that have been compiled. This can be very powerful, but is not -something needed in everyday use. +interface. The compiler pass gives you an opportunity to manipulate the +service definitions that have been compiled. This can be very powerful, +but is not something needed in everyday use. The compiler pass must have the ``process`` method which is passed the container being compiled:: @@ -330,9 +336,9 @@ being compiled:: The container's parameters and definitions can be manipulated using the methods described in the :doc:`/components/dependency_injection/definitions`. -One common thing to do in a compiler pass is to search for all services that -have a certain tag in order to process them in some way or dynamically plug -each into some other service. +One common thing to do in a compiler pass is to search for all services +that have a certain tag in order to process them in some way or dynamically +plug each into some other service. Registering a Compiler Pass --------------------------- @@ -356,9 +362,10 @@ Controlling the Pass Ordering The default compiler passes are grouped into optimization passes and removal passes. The optimization passes run first and include tasks such as resolving -references within the definitions. The removal passes perform tasks such as removing -private aliases and unused services. You can choose where in the order any custom -passes you add are run. By default they will be run before the optimization passes. +references within the definitions. The removal passes perform tasks such +as removing private aliases and unused services. You can choose where in +the order any custom passes you add are run. By default they will be run +before the optimization passes. You can use the following constants as the second argument when registering a pass with the container to control where it goes in the order: @@ -369,7 +376,8 @@ a pass with the container to control where it goes in the order: * ``PassConfig::TYPE_REMOVE`` * ``PassConfig::TYPE_AFTER_REMOVING`` -For example, to run your custom pass after the default removal passes have been run:: +For example, to run your custom pass after the default removal passes have +been run:: use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\PassConfig; @@ -386,12 +394,13 @@ Dumping the Configuration for Performance ----------------------------------------- Using configuration files to manage the service container can be much easier -to understand than using PHP once there are a lot of services. This ease comes -at a price though when it comes to performance as the config files need to be -parsed and the PHP configuration built from them. The compilation process makes -the container more efficient but it takes time to run. You can have the best of both -worlds though by using configuration files and then dumping and caching the resulting -configuration. The ``PhpDumper`` makes dumping the compiled container easy:: +to understand than using PHP once there are a lot of services. This ease +comes at a price though when it comes to performance as the config files +need to be parsed and the PHP configuration built from them. The compilation +process makes the container more efficient but it takes time to run. You +can have the best of both worlds though by using configuration files and +then dumping and caching the resulting configuration. The ``PhpDumper`` +makes dumping the compiled container easy:: use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; @@ -411,8 +420,8 @@ configuration. The ``PhpDumper`` makes dumping the compiled container easy:: } ``ProjectServiceContainer`` is the default name given to the dumped container -class, you can change this though this with the ``class`` option when you dump -it:: +class, you can change this though this with the ``class`` option when you +dump it:: // ... $file = __DIR__ .'/cache/container.php'; @@ -432,14 +441,15 @@ it:: ); } -You will now get the speed of the PHP configured container with the ease of using -configuration files. Additionally dumping the container in this way further optimizes -how the services are created by the container. +You will now get the speed of the PHP configured container with the ease +of using configuration files. Additionally dumping the container in this +way further optimizes how the services are created by the container. In the above example you will need to delete the cached container file whenever -you make any changes. Adding a check for a variable that determines if you are -in debug mode allows you to keep the speed of the cached container in production -but getting an up to date configuration whilst developing your application:: +you make any changes. Adding a check for a variable that determines if you +are in debug mode allows you to keep the speed of the cached container in +production but getting an up to date configuration whilst developing your +application:: // ... @@ -472,11 +482,11 @@ the container in the way described in ":doc:`/components/config/caching`" in the config component documentation. You do not need to work out which files to cache as the container builder -keeps track of all the resources used to configure it, not just the configuration -files but the extension classes and compiler passes as well. This means that -any changes to any of these files will invalidate the cache and trigger the -container being rebuilt. You just need to ask the container for these resources -and use them as metadata for the cache:: +keeps track of all the resources used to configure it, not just the +configuration files but the extension classes and compiler passes as well. +This means that any changes to any of these files will invalidate the cache +and trigger the container being rebuilt. You just need to ask the container +for these resources and use them as metadata for the cache:: // ... @@ -501,12 +511,13 @@ and use them as metadata for the cache:: require_once $file; $container = new MyCachedContainer(); -Now the cached dumped container is used regardless of whether debug mode is on or not. -The difference is that the ``ConfigCache`` is set to debug mode with its second -constructor argument. When the cache is not in debug mode the cached container -will always be used if it exists. In debug mode, an additional metadata file -is written with the timestamps of all the resource files. These are then checked -to see if the files have changed, if they have the cache will be considered stale. +Now the cached dumped container is used regardless of whether debug mode +is on or not. The difference is that the ``ConfigCache`` is set to debug +mode with its second constructor argument. When the cache is not in debug +mode the cached container will always be used if it exists. In debug mode, +an additional metadata file is written with the timestamps of all the resource +files. These are then checked to see if the files have changed, if they +have the cache will be considered stale. .. note:: diff --git a/components/dependency_injection/configurators.rst b/components/dependency_injection/configurators.rst index c8036fc8685..02ccf800af4 100644 --- a/components/dependency_injection/configurators.rst +++ b/components/dependency_injection/configurators.rst @@ -4,27 +4,28 @@ Configuring Services with a Service Configurator ================================================ -The Service Configurator is a feature of the Dependency Injection Container that -allows you to use a callable to configure a service after its instantiation. +The Service Configurator is a feature of the Dependency Injection Container +that allows you to use a callable to configure a service after its instantiation. -You can specify a method in another service, a PHP function or a static method -in a class. The service instance is passed to the callable, allowing the -configurator to do whatever it needs to configure the service after its -creation. +You can specify a method in another service, a PHP function or a static +method in a class. The service instance is passed to the callable, allowing +the configurator to do whatever it needs to configure the service after +its creation. -A Service Configurator can be used, for example, when you have a service that -requires complex setup based on configuration settings coming from different -sources/services. Using an external configurator, you can maintain the service -implementation cleanly and keep it decoupled from the other objects that provide -the configuration needed. +A Service Configurator can be used, for example, when you have a service +that requires complex setup based on configuration settings coming from +different sources/services. Using an external configurator, you can maintain +the service implementation cleanly and keep it decoupled from the other +objects that provide the configuration needed. -Another interesting use case is when you have multiple objects that share a -common configuration or that should be configured in a similar way at runtime. +Another interesting use case is when you have multiple objects that +share a common configuration or that should be configured in a similar way +at runtime. -For example, suppose you have an application where you send different types of -emails to users. Emails are passed through different formatters that could be -enabled or not depending on some dynamic application settings. You start -defining a ``NewsletterManager`` class like this:: +For example, suppose you have an application where you send different types +of emails to users. Emails are passed through different formatters that +could be enabled or not depending on some dynamic application settings. +You start defining a ``NewsletterManager`` class like this:: class NewsletterManager implements EmailFormatterAwareInterface { @@ -64,8 +65,8 @@ and also a ``GreetingCardManager`` class:: // ... } -As mentioned before, the goal is to set the formatters at runtime depending on -application settings. To do this, you also have an ``EmailFormatterManager`` +As mentioned before, the goal is to set the formatters at runtime depending +on application settings. To do this, you also have an ``EmailFormatterManager`` class which is responsible for loading and validating formatters enabled in the application:: @@ -91,8 +92,8 @@ in the application:: } If your goal is to avoid having to couple ``NewsletterManager`` and -``GreetingCardManager`` with ``EmailFormatterManager``, then you might want to -create a configurator class to configure these instances:: +``GreetingCardManager`` with ``EmailFormatterManager``, then you might want +to create a configurator class to configure these instances:: class EmailConfigurator { @@ -115,9 +116,9 @@ create a configurator class to configure these instances:: The ``EmailConfigurator``'s job is to inject the enabled filters into ``NewsletterManager`` and ``GreetingCardManager`` because they are not aware of where the enabled -filters come from. In the other hand, the ``EmailFormatterManager`` holds the -knowledge about the enabled formatters and how to load them, keeping the single -responsibility principle. +filters come from. In the other hand, the ``EmailFormatterManager`` holds +the knowledge about the enabled formatters and how to load them, keeping +the single responsibility principle. Configurator Service Config --------------------------- diff --git a/components/dependency_injection/definitions.rst b/components/dependency_injection/definitions.rst index 9c9574c7b9b..9205de8fb6a 100644 --- a/components/dependency_injection/definitions.rst +++ b/components/dependency_injection/definitions.rst @@ -1,5 +1,5 @@ .. index:: - single: DependencyInjection; Service definitions + single: DependencyInjection; Service definitions Working with Container Service Definitions ========================================== @@ -13,7 +13,8 @@ To find out if there is a definition for a service id:: $container->hasDefinition($serviceId); -This is useful if you only want to do something if a particular definition exists. +This is useful if you only want to do something if a particular definition +exists. You can retrieve a definition with:: @@ -36,7 +37,7 @@ it to the container using:: Working with a Definition ------------------------- -Creating a new Definition +Creating a New Definition ~~~~~~~~~~~~~~~~~~~~~~~~~ If you need to create a new definition rather than manipulate one retrieved @@ -104,11 +105,12 @@ Add a method call with:: $definition->addMethodCall($method, $arguments); -Where ``$method`` is the method name and ``$arguments`` is an array of the arguments -to call the method with. The arguments can be strings, arrays, parameters or -service ids as with the constructor arguments. +Where ``$method`` is the method name and ``$arguments`` is an array of the +arguments to call the method with. The arguments can be strings, arrays, +parameters or service ids as with the constructor arguments. -You can also replace any existing method calls with an array of new ones with:: +You can also replace any existing method calls with an array of new ones +with:: $definition->setMethodCalls($methodCalls); diff --git a/components/dependency_injection/factories.rst b/components/dependency_injection/factories.rst index 7a22ba17da2..8807975d5f8 100644 --- a/components/dependency_injection/factories.rst +++ b/components/dependency_injection/factories.rst @@ -8,9 +8,9 @@ Symfony's Service Container provides a powerful way of controlling the creation of objects, allowing you to specify arguments passed to the constructor as well as calling methods and setting parameters. Sometimes, however, this will not provide you with everything you need to construct your objects. -For this situation, you can use a factory to create the object and tell the -service container to call a method on the factory rather than directly instantiating -the class. +For this situation, you can use a factory to create the object and tell +the service container to call a method on the factory rather than directly +instantiating the class. Suppose you have a factory that configures and returns a new ``NewsletterManager`` object:: @@ -28,8 +28,8 @@ object:: } To make the ``NewsletterManager`` object available as a service, you can -configure the service container to use the ``NewsletterManagerFactory`` factory -class: +configure the service container to use the ``NewsletterManagerFactory`` +factory class: .. configuration-block:: @@ -71,16 +71,16 @@ class: .. note:: When using a factory to create services, the value chosen for the ``class`` - option has no effect on the resulting service. The actual class name only - depends on the object that is returned by the factory. However, the configured - class name may be used by compiler passes and therefore should be set to a - sensible value. + option has no effect on the resulting service. The actual class name + only depends on the object that is returned by the factory. However, + the configured class name may be used by compiler passes and therefore + should be set to a sensible value. When you specify the class to use for the factory (via ``factory_class``) the method will be called statically. If the factory itself should be instantiated -and the resulting object's method called, configure the factory itself as a service. -In this case, the method (e.g. ``createNewsletterManager``) should be changed -to be non-static: +and the resulting object's method called, configure the factory itself +as a service. In this case, the method (e.g. ``createNewsletterManager``) +should be changed to be non-static: .. configuration-block:: @@ -129,9 +129,9 @@ to be non-static: .. note:: - The factory service is specified by its id name and not a reference to - the service itself. So, you do not need to use the @ syntax for this in - YAML configurations. + The factory service is specified by its id name and not a reference + to the service itself. So, you do not need to use the ``@`` syntax for + this in YAML configurations. Passing Arguments to the Factory Method --------------------------------------- diff --git a/components/dependency_injection/index.rst b/components/dependency_injection/index.rst index dfa2e1ef54b..313f29af3f4 100644 --- a/components/dependency_injection/index.rst +++ b/components/dependency_injection/index.rst @@ -1,4 +1,4 @@ -DependencyInjection +DependencyInjection =================== .. toctree:: diff --git a/components/dependency_injection/introduction.rst b/components/dependency_injection/introduction.rst index 58fe212780d..880b56efe95 100644 --- a/components/dependency_injection/introduction.rst +++ b/components/dependency_injection/introduction.rst @@ -16,7 +16,8 @@ Installation You can install the component in 2 different ways: -* :doc:`Install it via Composer ` (``symfony/dependency-injection`` on `Packagist`_); +* :doc:`Install it via Composer ` (``symfony/dependency-injection`` + on `Packagist`_); * Use the official Git repository (https://github.com/symfony/DependencyInjection). .. include:: /components/require_autoload.rst.inc @@ -74,10 +75,10 @@ Then you can set the choice of transport in the container:: This class is now much more flexible as you have separated the choice of transport out of the implementation and into the container. -Which mail transport you have chosen may be something other services need to -know about. You can avoid having to change it in multiple places by making -it a parameter in the container and then referring to this parameter for the -``Mailer`` service's constructor argument:: +Which mail transport you have chosen may be something other services need +to know about. You can avoid having to change it in multiple places by making +it a parameter in the container and then referring to this parameter for +the ``Mailer`` service's constructor argument:: use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -182,10 +183,10 @@ Setting up the Container with Configuration Files As well as setting up the services using PHP as above you can also use configuration files. This allows you to use XML or YAML to write the definitions -for the services rather than using PHP to define the services as in the above -examples. In anything but the smallest applications it makes sense to organize -the service definitions by moving them into one or more configuration files. -To do this you also need to install +for the services rather than using PHP to define the services as in the +above examples. In anything but the smallest applications it makes sense +to organize the service definitions by moving them into one or more configuration +files. To do this you also need to install :doc:`the Config component `. Loading an XML config file:: diff --git a/components/dependency_injection/lazy_services.rst b/components/dependency_injection/lazy_services.rst index d527d02db98..3ed6fb33678 100644 --- a/components/dependency_injection/lazy_services.rst +++ b/components/dependency_injection/lazy_services.rst @@ -7,7 +7,7 @@ Lazy Services .. versionadded:: 2.3 Lazy services were introduced in Symfony 2.3. -Why lazy Services? +Why Lazy Services? ------------------ In some cases, you may want to inject a service that is a bit heavy to instantiate, @@ -17,10 +17,10 @@ a few methods on your ``NewsletterManager`` actually use the ``mailer``, but even when you don't need it, a ``mailer`` service is always instantiated in order to construct your ``NewsletterManager``. -Configuring lazy services is one answer to this. With a lazy service, a "proxy" -of the ``mailer`` service is actually injected. It looks and acts just like -the ``mailer``, except that the ``mailer`` isn't actually instantiated until -you interact with the proxy in some way. +Configuring lazy services is one answer to this. With a lazy service, a +"proxy" of the ``mailer`` service is actually injected. It looks and acts +just like the ``mailer``, except that the ``mailer`` isn't actually instantiated +until you interact with the proxy in some way. Installation ------------ @@ -34,8 +34,9 @@ the `ProxyManager bridge`_: .. note:: - If you're using the full-stack framework, the proxy manager bridge is already - included but the actual proxy manager needs to be included. So, run: + If you're using the full-stack framework, the proxy manager bridge is + already included but the actual proxy manager needs to be included. + So, run: .. code-block:: bash @@ -94,14 +95,14 @@ received object. var_dump(class_implements($service)); -If the class implements the ``ProxyManager\Proxy\LazyLoadingInterface`` your -lazy loaded services are working. +If the class implements the ``ProxyManager\Proxy\LazyLoadingInterface`` +your lazy loaded services are working. .. note:: - If you don't install the `ProxyManager bridge`_, the container will just - skip over the ``lazy`` flag and simply instantiate the service as it would - normally do. + If you don't install the `ProxyManager bridge`_, the container will + just skip over the ``lazy`` flag and simply instantiate the service + as it would normally do. The proxy gets initialized and the actual service is instantiated as soon as you interact in any way with this object. diff --git a/components/dependency_injection/parameters.rst b/components/dependency_injection/parameters.rst index 1ceb887a138..604cf00f704 100644 --- a/components/dependency_injection/parameters.rst +++ b/components/dependency_injection/parameters.rst @@ -29,8 +29,8 @@ and set a parameter in the container with:: The used ``.`` notation is just a :ref:`Symfony convention ` to make parameters - easier to read. Parameters are just flat key-value elements, they can't be - organized into a nested array + easier to read. Parameters are just flat key-value elements, they can't + be organized into a nested array .. note:: @@ -67,14 +67,15 @@ You can also use the ``parameters`` section of a config file to set parameters: $container->setParameter('mailer.transport', 'sendmail'); As well as retrieving the parameter values directly from the container you -can use them in the config files. You can refer to parameters elsewhere by -surrounding them with percent (``%``) signs, e.g. ``%mailer.transport%``. +can use them in the config files. You can refer to parameters elsewhere +by surrounding them with percent (``%``) signs, e.g. ``%mailer.transport%``. One use for this is to inject the values into your services. This allows -you to configure different versions of services between applications or multiple -services based on the same class but configured differently within a single -application. You could inject the choice of mail transport into the ``Mailer`` -class directly. But declaring it as a parameter makes it easier to change -rather than being tied up and hidden with the service definition: +you to configure different versions of services between applications or +multiple services based on the same class but configured differently +within a single application. You could inject the choice of mail transport +into the ``Mailer`` class directly. But declaring it as a parameter makes +it easier to change rather than being tied up and hidden with the service +definition: .. configuration-block:: @@ -118,8 +119,8 @@ rather than being tied up and hidden with the service definition: .. caution:: - The values between ``parameter`` tags in XML configuration files are not - trimmed. + The values between ``parameter`` tags in XML configuration files are + not trimmed. This means that the following configuration sample will have the value ``\n sendmail\n``: @@ -130,8 +131,9 @@ rather than being tied up and hidden with the service definition: sendmail - In some cases (for constants or class names), this could throw errors. In - order to prevent this, you must always inline your parameters as follow: + In some cases (for constants or class names), this could throw errors. + In order to prevent this, you must always inline your parameters as + follow: .. code-block:: xml @@ -142,8 +144,8 @@ the parameter value in one place if needed. .. note:: - The percent sign inside a parameter or argument, as part of the string, must - be escaped with another percent sign: + The percent sign inside a parameter or argument, as part of the string, + must be escaped with another percent sign: .. configuration-block:: @@ -165,8 +167,8 @@ Array Parameters ---------------- Parameters do not need to be flat strings, they can also contain array values. -For the XML format, you need to use the ``type="collection"`` attribute for -all parameters that are arrays. +For the XML format, you need to use the ``type="collection"`` attribute +for all parameters that are arrays. .. configuration-block:: @@ -224,9 +226,9 @@ all parameters that are arrays. Constants as Parameters ----------------------- -The container also has support for setting PHP constants as parameters. To -take advantage of this feature, map the name of your constant to a parameter -key, and define the type as ``constant``. +The container also has support for setting PHP constants as parameters. +To take advantage of this feature, map the name of your constant to a parameter +key and define the type as ``constant``. .. configuration-block:: @@ -261,8 +263,8 @@ key, and define the type as ``constant``. PHP Keywords in XML ------------------- -By default, ``true``, ``false`` and ``null`` in XML are converted to the PHP -keywords (respectively ``true``, ``false`` and ``null``): +By default, ``true``, ``false`` and ``null`` in XML are converted to the +PHP keywords (respectively ``true``, ``false`` and ``null``): .. code-block:: xml diff --git a/components/dependency_injection/parentservices.rst b/components/dependency_injection/parentservices.rst index b7729ab1f50..9237e57ac21 100644 --- a/components/dependency_injection/parentservices.rst +++ b/components/dependency_injection/parentservices.rst @@ -1,12 +1,13 @@ .. index:: - single: DependencyInjection; Parent services + single: DependencyInjection; Parent services -Managing common Dependencies with parent Services +Managing Common Dependencies with Parent Services ================================================= -As you add more functionality to your application, you may well start to have -related classes that share some of the same dependencies. For example you -may have a Newsletter Manager which uses setter injection to set its dependencies:: +As you add more functionality to your application, you may well start to +have related classes that share some of the same dependencies. For example, +you may have a Newsletter Manager which uses setter injection to set its +dependencies:: class NewsletterManager { @@ -136,12 +137,13 @@ The service config for these classes would look something like this: )) ; -There is a lot of repetition in both the classes and the configuration. This -means that if you changed, for example, the ``Mailer`` of ``EmailFormatter`` -classes to be injected via the constructor, you would need to update the config -in two places. Likewise if you needed to make changes to the setter methods -you would need to do this in both classes. The typical way to deal with the -common methods of these related classes would be to extract them to a super class:: +There is a lot of repetition in both the classes and the configuration. +This means that if you changed, for example, the ``Mailer`` of +``EmailFormatter`` classes to be injected via the constructor, you would +need to update the config in two places. Likewise if you needed to make +changes to the setter methods you would need to do this in both classes. +The typical way to deal with the common methods of these related classes +would be to extract them to a super class:: abstract class MailManager { @@ -260,10 +262,10 @@ a parent for a service. $greetingCardManager->setClass('GreetingCardManager'); $container->setDefinition('greeting_card_manager', $greetingCardManager); -In this context, having a ``parent`` service implies that the arguments and -method calls of the parent service should be used for the child services. -Specifically, the setter methods defined for the parent service will be called -when the child services are instantiated. +In this context, having a ``parent`` service implies that the arguments +and method calls of the parent service should be used for the child services. +Specifically, the setter methods defined for the parent service will be +called when the child services are instantiated. .. note:: @@ -275,19 +277,19 @@ when the child services are instantiated. .. caution:: - The ``scope``, ``abstract`` and ``tags`` attributes are always taken from - the child service. + The ``scope``, ``abstract`` and ``tags`` attributes are always taken + from the child service. -The parent service is abstract as it should not be directly retrieved from the -container or passed into another service. It exists merely as a "template" that -other services can use. This is why it can have no ``class`` configured which -would cause an exception to be raised for a non-abstract service. +The parent service is abstract as it should not be directly retrieved from +the container or passed into another service. It exists merely as a "template" +that other services can use. This is why it can have no ``class`` configured +which would cause an exception to be raised for a non-abstract service. .. note:: - In order for parent dependencies to resolve, the ``ContainerBuilder`` must - first be compiled. See :doc:`/components/dependency_injection/compilation` - for more details. + In order for parent dependencies to resolve, the ``ContainerBuilder`` + must first be compiled. See + :doc:`/components/dependency_injection/compilation` for more details. .. tip:: @@ -296,7 +298,7 @@ would cause an exception to be raised for a non-abstract service. You can just extract common parts of similar service definitions into a parent service without also extending a parent class in PHP. -Overriding parent Dependencies +Overriding Parent Dependencies ------------------------------ There may be times where you want to override what class is passed in for @@ -411,10 +413,10 @@ instead of the ``my_mailer`` service. .. caution:: - You can't override method calls. When you defined new method calls in the child - service, it'll be added to the current set of configured method calls. This means - it works perfectly when the setter overrides the current property, but it doesn't - work as expected when the setter appends it to the existing data (e.g. an - ``addFilters()`` method). - In those cases, the only solution is to *not* extend the parent service and configuring + You can't override method calls. When you defined new method calls in + the child service, it'll be added to the current set of configured method + calls. This means it works perfectly when the setter overrides the current + property, but it doesn't work as expected when the setter appends it + to the existing data (e.g. an ``addFilters()`` method). In those cases, + the only solution is to *not* extend the parent service and configuring the service just like you did before knowing this feature. diff --git a/components/dependency_injection/synthetic_services.rst b/components/dependency_injection/synthetic_services.rst index cbe32a8c60a..d53e4636bdc 100644 --- a/components/dependency_injection/synthetic_services.rst +++ b/components/dependency_injection/synthetic_services.rst @@ -4,8 +4,8 @@ How to Inject Instances into the Container ------------------------------------------ -When using the container in your application, you sometimes need to inject an -instance instead of configuring the container to create a new instance. +When using the container in your application, you sometimes need to inject +an instance instead of configuring the container to create a new instance. For instance, if you're using the :doc:`HttpKernel ` component with the DependencyInjection component, then the ``kernel`` @@ -24,10 +24,10 @@ service is injected into the container from within the ``Kernel`` class:: } } -The ``kernel`` service is called a synthetic service. This service has to be -configured in the container, so the container knows the service does exist -during compilation (otherwise, services depending on this ``kernel`` service -will get a "service does not exists" error). +The ``kernel`` service is called a synthetic service. This service has to +be configured in the container, so the container knows the service does +exist during compilation (otherwise, services depending on this ``kernel`` +service will get a "service does not exists" error). In order to do so, you have to use :method:`Definition::setSynthetic() `:: diff --git a/components/dependency_injection/tags.rst b/components/dependency_injection/tags.rst index 9bb0d000707..17b8b584e53 100644 --- a/components/dependency_injection/tags.rst +++ b/components/dependency_injection/tags.rst @@ -4,12 +4,12 @@ Working with Tagged Services ============================ -Tags are a generic string (along with some options) that can be applied to -any service. By themselves, tags don't actually alter the functionality of your -services in any way. But if you choose to, you can ask a container builder -for a list of all services that were tagged with some specific tag. This -is useful in compiler passes where you can find these services and use or -modify them in some specific way. +Tags are a generic string (along with some options) that can be applied +to any service. By themselves, tags don't actually alter the functionality +of your services in any way. But if you choose to, you can ask a container +builder for a list of all services that were tagged with some specific tag. +This is useful in compiler passes where you can find these services and +use or modify them in some specific way. For example, if you are using Swift Mailer you might imagine that you want to implement a "transport chain", which is a collection of classes implementing @@ -57,11 +57,9 @@ Then, define the chain as a service: .. code-block:: php - use Symfony\Component\DependencyInjection\Definition; - - $container->setDefinition('acme_mailer.transport_chain', new Definition('TransportChain')); + $container->register('acme_mailer.transport_chain', 'TransportChain'); -Define Services with a custom Tag +Define Services with a Custom Tag --------------------------------- Now you might want several of the ``\Swift_Transport`` classes to be instantiated @@ -154,11 +152,11 @@ custom tag:: } The ``process()`` method checks for the existence of the ``acme_mailer.transport_chain`` -service, then looks for all services tagged ``acme_mailer.transport``. It adds -to the definition of the ``acme_mailer.transport_chain`` service a call to -``addTransport()`` for each "acme_mailer.transport" service it has found. -The first argument of each of these calls will be the mailer transport service -itself. +service, then looks for all services tagged ``acme_mailer.transport``. It +adds to the definition of the ``acme_mailer.transport_chain`` service a +call to ``addTransport()`` for each "acme_mailer.transport" service it has +found. The first argument of each of these calls will be the mailer transport +service itself. Register the Pass with the Container ------------------------------------ @@ -177,11 +175,12 @@ run when the container is compiled:: framework. See :doc:`/cookbook/service_container/compiler_passes` for more details. -Adding additional Attributes on Tags +Adding Additional Attributes on Tags ------------------------------------ -Sometimes you need additional information about each service that's tagged with your tag. -For example, you might want to add an alias to each member of the transport chain. +Sometimes you need additional information about each service that's tagged +with your tag. For example, you might want to add an alias to each member +of the transport chain. To begin with, change the ``TransportChain`` class:: @@ -293,7 +292,7 @@ use this, update the compiler:: } } -The double loop may be confusing. This is because a service can have more than one -tag. You tag a service twice or more with the ``acme_mailer.transport`` tag. The -second foreach loop iterates over the ``acme_mailer.transport`` tags set for the -current service and gives you the attributes. +The double loop may be confusing. This is because a service can have more +than one tag. You tag a service twice or more with the ``acme_mailer.transport`` +tag. The second foreach loop iterates over the ``acme_mailer.transport`` +tags set for the current service and gives you the attributes. diff --git a/components/dependency_injection/types.rst b/components/dependency_injection/types.rst index 0a1f65304b8..c7181396218 100644 --- a/components/dependency_injection/types.rst +++ b/components/dependency_injection/types.rst @@ -9,8 +9,8 @@ into it is a good way of making a class more reusable, testable and decoupled from others. There are several ways that the dependencies can be injected. Each injection -point has advantages and disadvantages to consider, as well as different ways -of working with them when using the service container. +point has advantages and disadvantages to consider, as well as different +ways of working with them when using the service container. Constructor Injection --------------------- @@ -89,19 +89,20 @@ There are several advantages to using constructor injection: then injecting it via the constructor ensures it is present when the class is used as the class cannot be constructed without it. -* The constructor is only ever called once when the object is created, so you - can be sure that the dependency will not change during the object's lifetime. +* The constructor is only ever called once when the object is created, so + you can be sure that the dependency will not change during the object's + lifetime. -These advantages do mean that constructor injection is not suitable for working -with optional dependencies. It is also more difficult to use in combination -with class hierarchies: if a class uses constructor injection then extending it -and overriding the constructor becomes problematic. +These advantages do mean that constructor injection is not suitable for +working with optional dependencies. It is also more difficult to use in +combination with class hierarchies: if a class uses constructor injection +then extending it and overriding the constructor becomes problematic. Setter Injection ---------------- -Another possible injection point into a class is by adding a setter method that -accepts the dependency:: +Another possible injection point into a class is by adding a setter method +that accepts the dependency:: class NewsletterManager { @@ -159,19 +160,19 @@ accepts the dependency:: This time the advantages are: -* Setter injection works well with optional dependencies. If you do not need - the dependency, then just do not call the setter. +* Setter injection works well with optional dependencies. If you do not + need the dependency, then just do not call the setter. -* You can call the setter multiple times. This is particularly useful if the - method adds the dependency to a collection. You can then have a variable number - of dependencies. +* You can call the setter multiple times. This is particularly useful if + the method adds the dependency to a collection. You can then have a variable + number of dependencies. The disadvantages of setter injection are: * The setter can be called more than just at the time of construction so - you cannot be sure the dependency is not replaced during the lifetime of the - object (except by explicitly writing the setter method to check if it has already - been called). + you cannot be sure the dependency is not replaced during the lifetime + of the object (except by explicitly writing the setter method to check + if it has already been called). * You cannot be sure the setter will be called and so you need to add checks that any required dependencies are injected. diff --git a/components/dependency_injection/workflow.rst b/components/dependency_injection/workflow.rst index d528c10fa6b..77c5a10813b 100644 --- a/components/dependency_injection/workflow.rst +++ b/components/dependency_injection/workflow.rst @@ -13,21 +13,21 @@ whether you are using the full-stack framework or looking to use the service container in another application. The full-stack framework uses the HttpKernel component to manage the loading -of the service container configuration from the application and bundles and -also handles the compilation and caching. Even if you are not using HttpKernel, -it should give you an idea of one way of organizing configuration in a modular -application. +of the service container configuration from the application and bundles +and also handles the compilation and caching. Even if you are not using +HttpKernel, it should give you an idea of one way of organizing configuration +in a modular application. Working with a Cached Container ------------------------------- -Before building it, the kernel checks to see if a cached version of the container -exists. The HttpKernel has a debug setting and if this is false, the -cached version is used if it exists. If debug is true then the kernel +Before building it, the kernel checks to see if a cached version of the +container exists. The HttpKernel has a debug setting and if this is false, +the cached version is used if it exists. If debug is true then the kernel :doc:`checks to see if configuration is fresh ` -and if it is, the cached version of the container is used. If not then the container -is built from the application-level configuration and the bundles's extension -configuration. +and if it is, the cached version of the container is used. If not then the +container is built from the application-level configuration and the bundles's +extension configuration. Read :ref:`Dumping the Configuration for Performance ` for more details. @@ -36,11 +36,13 @@ Application-level Configuration ------------------------------- Application level config is loaded from the ``app/config`` directory. Multiple -files are loaded which are then merged when the extensions are processed. This -allows for different configuration for different environments e.g. dev, prod. +files are loaded which are then merged when the extensions are processed. +This allows for different configuration for different environments e.g. +dev, prod. These files contain parameters and services that are loaded directly into -the container as per :ref:`Setting Up the Container with Configuration Files `. +the container as per +:ref:`Setting Up the Container with Configuration Files `. They also contain configuration that is processed by extensions as per :ref:`Managing Configuration with Extensions `. These are considered to be bundle configuration since each bundle contains @@ -51,28 +53,29 @@ Bundle-level Configuration with Extensions By convention, each bundle contains an Extension class which is in the bundle's ``DependencyInjection`` directory. These are registered with the ``ContainerBuilder`` -when the kernel is booted. When the ``ContainerBuilder`` is :doc:`compiled `, -the application-level configuration relevant to the bundle's extension is -passed to the Extension which also usually loads its own config file(s), typically from the bundle's -``Resources/config`` directory. The application-level config is usually processed -with a :doc:`Configuration object ` also stored -in the bundle's ``DependencyInjection`` directory. +when the kernel is booted. When the ``ContainerBuilder`` is +:doc:`compiled `, the application-level +configuration relevant to the bundle's extension is passed to the Extension +which also usually loads its own config file(s), typically from the bundle's +``Resources/config`` directory. The application-level config is usually +processed with a :doc:`Configuration object ` +also stored in the bundle's ``DependencyInjection`` directory. Compiler Passes to Allow Interaction between Bundles ---------------------------------------------------- -:ref:`Compiler passes ` are -used to allow interaction between different bundles as they cannot affect -each other's configuration in the extension classes. One of the main uses is -to process tagged services, allowing bundles to register services to be picked -up by other bundles, such as Monolog loggers, Twig extensions and Data Collectors -for the Web Profiler. Compiler passes are usually placed in the bundle's -``DependencyInjection/Compiler`` directory. +:ref:`Compiler passes ` +are used to allow interaction between different bundles as they cannot affect +each other's configuration in the extension classes. One of the main uses +is to process tagged services, allowing bundles to register services to +be picked up by other bundles, such as Monolog loggers, Twig extensions +and Data Collectors for the Web Profiler. Compiler passes are usually placed +in the bundle's ``DependencyInjection/Compiler`` directory. Compilation and Caching ----------------------- After the compilation process has loaded the services from the configuration, -extensions and the compiler passes, it is dumped so that the cache can be used -next time. The dumped version is then used during subsequent requests as it -is more efficient. +extensions and the compiler passes, it is dumped so that the cache can be +used next time. The dumped version is then used during subsequent requests +as it is more efficient. From a045995ea52094b664a494a9a52ed543e3de28fd Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 21 Jul 2015 18:34:52 +0200 Subject: [PATCH 0358/2667] Fix BOM characters --- components/dependency_injection/compilation.rst | 3 ++- components/dependency_injection/definitions.rst | 3 ++- components/dependency_injection/introduction.rst | 3 ++- components/dependency_injection/tags.rst | 3 ++- components/dependency_injection/types.rst | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/components/dependency_injection/compilation.rst b/components/dependency_injection/compilation.rst index 34552dafe97..68ef2693883 100644 --- a/components/dependency_injection/compilation.rst +++ b/components/dependency_injection/compilation.rst @@ -1,4 +1,4 @@ -.. index:: +.. index:: single: DependencyInjection; Compilation Compiling the Container @@ -523,3 +523,4 @@ have the cache will be considered stale. In the full-stack framework the compilation and caching of the container is taken care of for you. + diff --git a/components/dependency_injection/definitions.rst b/components/dependency_injection/definitions.rst index 9205de8fb6a..bd13b766b43 100644 --- a/components/dependency_injection/definitions.rst +++ b/components/dependency_injection/definitions.rst @@ -1,4 +1,4 @@ -.. index:: +.. index:: single: DependencyInjection; Service definitions Working with Container Service Definitions @@ -139,3 +139,4 @@ the service itself gets loaded. To do so, you can use the Notice that Symfony will internally call the PHP statement ``require_once``, which means that your file will be included only once per request. + diff --git a/components/dependency_injection/introduction.rst b/components/dependency_injection/introduction.rst index 880b56efe95..f972a5b3578 100644 --- a/components/dependency_injection/introduction.rst +++ b/components/dependency_injection/introduction.rst @@ -1,4 +1,4 @@ -.. index:: +.. index:: single: DependencyInjection single: Components; DependencyInjection @@ -285,3 +285,4 @@ config files: ->addMethodCall('setMailer', array(new Reference('mailer'))); .. _Packagist: https://packagist.org/packages/symfony/dependency-injection + diff --git a/components/dependency_injection/tags.rst b/components/dependency_injection/tags.rst index 17b8b584e53..83deec45395 100644 --- a/components/dependency_injection/tags.rst +++ b/components/dependency_injection/tags.rst @@ -1,4 +1,4 @@ -.. index:: +.. index:: single: DependencyInjection; Tags Working with Tagged Services @@ -296,3 +296,4 @@ The double loop may be confusing. This is because a service can have more than one tag. You tag a service twice or more with the ``acme_mailer.transport`` tag. The second foreach loop iterates over the ``acme_mailer.transport`` tags set for the current service and gives you the attributes. + diff --git a/components/dependency_injection/types.rst b/components/dependency_injection/types.rst index c7181396218..f04781d8aad 100644 --- a/components/dependency_injection/types.rst +++ b/components/dependency_injection/types.rst @@ -1,4 +1,4 @@ -.. index:: +.. index:: single: DependencyInjection; Injection types Types of Injection @@ -242,3 +242,4 @@ to setter injection but with these additional important problems: But, it is useful to know that this can be done with the service container, especially if you are working with code that is out of your control, such as in a third party library, which uses public properties for its dependencies. + From ac46cbc54e798c87dd552434017b84f94d634590 Mon Sep 17 00:00:00 2001 From: Alfonso Machado Date: Fri, 17 Jul 2015 09:17:54 +0200 Subject: [PATCH 0359/2667] Update event-dispatcher.rst --- create_framework/event-dispatcher.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_framework/event-dispatcher.rst b/create_framework/event-dispatcher.rst index fce91ac59bd..9d6b8c1ba89 100644 --- a/create_framework/event-dispatcher.rst +++ b/create_framework/event-dispatcher.rst @@ -13,7 +13,7 @@ needs. Many software have a similar concept like Drupal or Wordpress. In some languages, there is even a standard like `WSGI`_ in Python or `Rack`_ in Ruby. As there is no standard for PHP, we are going to use a well-known design -pattern, the *Observer*, to allow any kind of behaviors to be attached to our +pattern, the *Mediator*, to allow any kind of behaviors to be attached to our framework; the Symfony EventDispatcher Component implements a lightweight version of this pattern: From d3f6c24523130c501ecd3a00ebf1265543fc60ea Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Tue, 21 Jul 2015 18:05:59 +0800 Subject: [PATCH 0360/2667] [components][expression_language] Fix the wrong constructor for SerializedParsedExpression There is a bug in the code snippet for creating a SerializedParsedExpression instance in chapter "Using Parsed and Serialized Expressions" | Q | A | ------------- | --- | Doc fix? | yes | New docs? | no | Applies to | all | Fixed tickets | --- components/expression_language/caching.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/expression_language/caching.rst b/components/expression_language/caching.rst index 4961d76bd6c..9c60ecc67bf 100644 --- a/components/expression_language/caching.rst +++ b/components/expression_language/caching.rst @@ -65,7 +65,7 @@ Both ``evaluate()`` and ``compile()`` can handle ``ParsedExpression`` and $expression = new SerializedParsedExpression( '1 + 4', - serialize($language->parse('1 + 4', array())) + serialize($language->parse('1 + 4', array())->getNodes()) ); var_dump($language->evaluate($expression)); // prints 5 From f0d4ea4674ca96371252d3eaa0810f09a213355d Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 22 Jul 2015 08:19:36 +0200 Subject: [PATCH 0361/2667] use the include() Twig function instead of the tag --- book/templating.rst | 4 ++-- cookbook/profiler/data_collector.rst | 10 +++++----- cookbook/templating/namespaced_paths.rst | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/book/templating.rst b/book/templating.rst index 62500e250bb..05c607eb4a8 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -584,9 +584,9 @@ you set `with_context`_ to false). maps (i.e. an array with named keys). If you needed to pass in multiple elements, it would look like this: ``{'foo': foo, 'bar': bar}``. -.. versionadded:: 2.2 +.. versionadded:: 2.3 The `include() function`_ is a new Twig feature that's available in Symfony - 2.2. Prior, the `{% include %} tag`_ tag was used. + 2.3. Prior, the `{% include %} tag`_ tag was used. .. index:: single: Templating; Embedding action diff --git a/cookbook/profiler/data_collector.rst b/cookbook/profiler/data_collector.rst index 985738aa5cb..74e7d07006a 100644 --- a/cookbook/profiler/data_collector.rst +++ b/cookbook/profiler/data_collector.rst @@ -131,15 +131,15 @@ following example can help you get started: {% endset %} - {# Set the "link" value to false if you do not have a big "panel" + {# Set the "link" value to false if you do not have a big "panel" section that yo 10000 u want to direct the user to. #} - {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': true } %} + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { 'link': true }) }} {% endblock %} {% block head %} - {# Optional, if you need your own JS or CSS files. #} - {{ parent() }} {# Use parent() to keep the default styles #} + {# Optional, if you need your own JS or CSS files. #} + {{ parent() }} {# Use parent() to keep the default styles #} {% endblock %} {% block menu %} @@ -151,7 +151,7 @@ following example can help you get started: {% endblock %} {% block panel %} - {# Optional, for showing the most details. #} + {# Optional, for showing the most details. #}

Example

Major information goes here diff --git a/cookbook/templating/namespaced_paths.rst b/cookbook/templating/namespaced_paths.rst index 3cccde0bf10..94c37f67fb4 100644 --- a/cookbook/templating/namespaced_paths.rst +++ b/cookbook/templating/namespaced_paths.rst @@ -18,14 +18,14 @@ Take the following paths as an example: .. code-block:: jinja {% extends "AppBundle::layout.html.twig" %} - {% include "AppBundle:Foo:bar.html.twig" %} + {{ include('AppBundle:Foo:bar.html.twig') }} With namespaced paths, the following works as well: .. code-block:: jinja {% extends "@App/layout.html.twig" %} - {% include "@App/Foo/bar.html.twig" %} + {{ include('@App/Foo/bar.html.twig') }} Both paths are valid and functional by default in Symfony. @@ -80,7 +80,7 @@ called ``sidebar.twig`` in that directory, you can use it easily: .. code-block:: jinja - {% include '@foo_bar/sidebar.twig' %} + {{ include('@foo_bar/sidebar.twig') }} Multiple Paths per Namespace ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -134,4 +134,4 @@ in the previous three directories: .. code-block:: jinja - {% include '@theme/header.twig' %} + {{ include('@theme/header.twig') }} From b85837f52358b292e13458dedc05fb367a2662d9 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Wed, 22 Jul 2015 10:34:23 +0200 Subject: [PATCH 0362/2667] Reviewed EventDispatcher docs --- .../container_aware_dispatcher.rst | 15 +- components/event_dispatcher/generic_event.rst | 27 ++- .../event_dispatcher/immutable_dispatcher.rst | 10 +- components/event_dispatcher/introduction.rst | 224 +++++++++--------- .../event_dispatcher/traceable_dispatcher.rst | 4 +- 5 files changed, 143 insertions(+), 137 deletions(-) diff --git a/components/event_dispatcher/container_aware_dispatcher.rst b/components/event_dispatcher/container_aware_dispatcher.rst index 82a502f582b..c73d249c994 100644 --- a/components/event_dispatcher/container_aware_dispatcher.rst +++ b/components/event_dispatcher/container_aware_dispatcher.rst @@ -7,14 +7,15 @@ The Container Aware Event Dispatcher Introduction ------------ -The :class:`Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher` is -a special ``EventDispatcher`` implementation which is coupled to the service container -that is part of :doc:`the DependencyInjection component `. +The :class:`Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher` +is a special ``EventDispatcher`` implementation which is coupled to the +service container that is part of +:doc:`the DependencyInjection component `. It allows services to be specified as event listeners making the ``EventDispatcher`` extremely powerful. -Services are lazy loaded meaning the services attached as listeners will only be -created if an event is dispatched that requires those listeners. +Services are lazy loaded meaning the services attached as listeners will +only be created if an event is dispatched that requires those listeners. Setup ----- @@ -34,8 +35,8 @@ Adding Listeners The ``ContainerAwareEventDispatcher`` can either load specified services directly or services that implement :class:`Symfony\\Component\\EventDispatcher\\EventSubscriberInterface`. -The following examples assume the service container has been loaded with any -services that are mentioned. +The following examples assume the service container has been loaded with +any services that are mentioned. .. note:: diff --git a/components/event_dispatcher/generic_event.rst b/components/event_dispatcher/generic_event.rst index 27d3723e994..c26ee546432 100644 --- a/components/event_dispatcher/generic_event.rst +++ b/components/event_dispatcher/generic_event.rst @@ -4,20 +4,21 @@ The Generic Event Object ======================== -The base :class:`Symfony\\Component\\EventDispatcher\\Event` class provided by the -EventDispatcher component is deliberately sparse to allow the creation of -API specific event objects by inheritance using OOP. This allows for elegant and -readable code in complex applications. +The base :class:`Symfony\\Component\\EventDispatcher\\Event` class provided +by the EventDispatcher component is deliberately sparse to allow the creation +of API specific event objects by inheritance using OOP. This allows for +elegant and readable code in complex applications. The :class:`Symfony\\Component\\EventDispatcher\\GenericEvent` is available -for convenience for those who wish to use just one event object throughout their -application. It is suitable for most purposes straight out of the box, because -it follows the standard observer pattern where the event object +for convenience for those who wish to use just one event object throughout +their application. It is suitable for most purposes straight out of the +box, because it follows the standard observer pattern where the event object encapsulates an event 'subject', but has the addition of optional extra arguments. -:class:`Symfony\\Component\\EventDispatcher\\GenericEvent` has a simple API in -addition to the base class :class:`Symfony\\Component\\EventDispatcher\\Event` +:class:`Symfony\\Component\\EventDispatcher\\GenericEvent` has a simple +API in addition to the base class +:class:`Symfony\\Component\\EventDispatcher\\Event` * :method:`Symfony\\Component\\EventDispatcher\\GenericEvent::__construct`: Constructor takes the event subject and any arguments; @@ -41,8 +42,8 @@ addition to the base class :class:`Symfony\\Component\\EventDispatcher\\Event` Returns true if the argument key exists; The ``GenericEvent`` also implements :phpclass:`ArrayAccess` on the event -arguments which makes it very convenient to pass extra arguments regarding the -event subject. +arguments which makes it very convenient to pass extra arguments regarding +the event subject. The following examples show use-cases to give a general idea of the flexibility. The examples assume event listeners have been added to the dispatcher. @@ -64,8 +65,8 @@ Simply passing a subject:: } } -Passing and processing arguments using the :phpclass:`ArrayAccess` API to access -the event arguments:: +Passing and processing arguments using the :phpclass:`ArrayAccess` API to +access the event arguments:: use Symfony\Component\EventDispatcher\GenericEvent; diff --git a/components/event_dispatcher/immutable_dispatcher.rst b/components/event_dispatcher/immutable_dispatcher.rst index bbcc167dc4a..7df2aecf282 100644 --- a/components/event_dispatcher/immutable_dispatcher.rst +++ b/components/event_dispatcher/immutable_dispatcher.rst @@ -7,13 +7,13 @@ The Immutable Event Dispatcher .. versionadded:: 2.1 This feature was introduced in Symfony 2.1. -The :class:`Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher` is -a locked or frozen event dispatcher. The dispatcher cannot register new +The :class:`Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher` +is a locked or frozen event dispatcher. The dispatcher cannot register new listeners or subscribers. -The ``ImmutableEventDispatcher`` takes another event dispatcher with all the -listeners and subscribers. The immutable dispatcher is just a proxy of this -original dispatcher. +The ``ImmutableEventDispatcher`` takes another event dispatcher with all +the listeners and subscribers. The immutable dispatcher is just a proxy +of this original dispatcher. To use it, first create a normal dispatcher (``EventDispatcher`` or ``ContainerAwareEventDispatcher``) and register some listeners or diff --git a/components/event_dispatcher/introduction.rst b/components/event_dispatcher/introduction.rst index e2c863bcafa..79071406086 100644 --- a/components/event_dispatcher/introduction.rst +++ b/components/event_dispatcher/introduction.rst @@ -6,44 +6,45 @@ The EventDispatcher Component ============================= The EventDispatcher component provides tools that allow your application - components to communicate with each other by dispatching events and listening - to them. + components to communicate with each other by dispatching events and + listening to them. Introduction ------------ -Object-oriented code has gone a long way to ensuring code extensibility. By -creating classes that have well defined responsibilities, your code becomes -more flexible and a developer can extend them with subclasses to modify their -behaviors. But if they want to share the changes with other developers who have -also made their own subclasses, code inheritance is no longer the answer. - -Consider the real-world example where you want to provide a plugin system for -your project. A plugin should be able to add methods, or do something before -or after a method is executed, without interfering with other plugins. This is -not an easy problem to solve with single inheritance, and multiple inheritance +Object-oriented code has gone a long way to ensuring code extensibility. +By creating classes that have well defined responsibilities, your code becomes +more flexible and a developer can extend them with subclasses to modify +their behaviors. But if they want to share the changes with other developers +who have also made their own subclasses, code inheritance is no longer the +answer. + +Consider the real-world example where you want to provide a plugin system +for your project. A plugin should be able to add methods, or do something +before or after a method is executed, without interfering with other plugins. +This is not an easy problem to solve with single and multiple inheritance (were it possible with PHP) has its own drawbacks. -The Symfony EventDispatcher component implements the `Mediator`_ pattern in -a simple and effective way to make all these things possible and to make your -projects truly extensible. +The Symfony EventDispatcher component implements the `Mediator`_ pattern +in a simple and effective way to make all these things possible and to make +your projects truly extensible. -Take a simple example from :doc:`/components/http_kernel/introduction`. Once a -``Response`` object has been created, it may be useful to allow other elements -in the system to modify it (e.g. add some cache headers) before it's actually -used. To make this possible, the Symfony kernel throws an event - -``kernel.response``. Here's how it works: +Take a simple example from :doc:`/components/http_kernel/introduction`. +Once a ``Response`` object has been created, it may be useful to allow other +elements in the system to modify it (e.g. add some cache headers) before +it's actually used. To make this possible, the Symfony kernel throws an +event - ``kernel.response``. Here's how it works: -* A *listener* (PHP object) tells a central *dispatcher* object that it wants - to listen to the ``kernel.response`` event; +* A *listener* (PHP object) tells a central *dispatcher* object that it + wants to listen to the ``kernel.response`` event; * At some point, the Symfony kernel tells the *dispatcher* object to dispatch - the ``kernel.response`` event, passing with it an ``Event`` object that has - access to the ``Response`` object; + the ``kernel.response`` event, passing with it an ``Event`` object that + has access to the ``Response`` object; * The dispatcher notifies (i.e. calls a method on) all listeners of the - ``kernel.response`` event, allowing each of them to make modifications to - the ``Response`` object. + ``kernel.response`` event, allowing each of them to make modifications + to the ``Response`` object. .. index:: single: EventDispatcher; Events @@ -53,7 +54,8 @@ Installation You can install the component in 2 different ways: -* :doc:`Install it via Composer ` (``symfony/event-dispatcher`` on `Packagist`_); +* :doc:`Install it via Composer ` + (``symfony/event-dispatcher`` on `Packagist`_); * Use the official Git repository (https://github.com/symfony/EventDispatcher). .. include:: /components/require_autoload.rst.inc @@ -65,10 +67,10 @@ Events ~~~~~~ When an event is dispatched, it's identified by a unique name (e.g. -``kernel.response``), which any number of listeners might be listening to. An -:class:`Symfony\\Component\\EventDispatcher\\Event` instance is also created -and passed to all of the listeners. As you'll see later, the ``Event`` object -itself often contains data about the event being dispatched. +``kernel.response``), which any number of listeners might be listening to. +An :class:`Symfony\\Component\\EventDispatcher\\Event` instance is also +created and passed to all of the listeners. As you'll see later, the ``Event`` +object itself often contains data about the event being dispatched. .. index:: pair: EventDispatcher; Naming conventions @@ -79,7 +81,7 @@ Naming Conventions The unique event name can be any string, but optionally follows a few simple naming conventions: -* use only lowercase letters, numbers, dots (``.``), and underscores (``_``); +* use only lowercase letters, numbers, dots (``.``) and underscores (``_``); * prefix names with a namespace followed by a dot (e.g. ``kernel.``); @@ -98,23 +100,24 @@ Event Names and Event Objects ............................. When the dispatcher notifies listeners, it passes an actual ``Event`` object -to those listeners. The base ``Event`` class is very simple: it contains a -method for stopping :ref:`event -propagation `, but not much else. +to those listeners. The base ``Event`` class is very simple: it +contains a method for stopping +:ref:`event propagation `, but not much +else. Often times, data about a specific event needs to be passed along with the -``Event`` object so that the listeners have needed information. In the case of -the ``kernel.response`` event, the ``Event`` object that's created and passed to -each listener is actually of type +``Event`` object so that the listeners have needed information. In the case +of the ``kernel.response`` event, the ``Event`` object that's created and +passed to each listener is actually of type :class:`Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent`, a -subclass of the base ``Event`` object. This class contains methods such as -``getResponse`` and ``setResponse``, allowing listeners to get or even replace -the ``Response`` object. +subclass of the base ``Event`` object. This class contains methods such +as ``getResponse`` and ``setResponse``, allowing listeners to get or even +replace the ``Response`` object. The moral of the story is this: When creating a listener to an event, the -``Event`` object that's passed to the listener may be a special subclass that -has additional methods for retrieving information from and responding to the -event. +``Event`` object that's passed to the listener may be a special subclass +that has additional methods for retrieving information from and responding +to the event. The Dispatcher ~~~~~~~~~~~~~~ @@ -134,10 +137,10 @@ listeners registered with that event:: Connecting Listeners ~~~~~~~~~~~~~~~~~~~~ -To take advantage of an existing event, you need to connect a listener to the -dispatcher so that it can be notified when the event is dispatched. A call to -the dispatcher's ``addListener()`` method associates any valid PHP callable to -an event:: +To take advantage of an existing event, you need to connect a listener to +the dispatcher so that it can be notified when the event is dispatched. +A call to the dispatcher's ``addListener()`` method associates any valid +PHP callable to an event:: $listener = new AcmeListener(); $dispatcher->addListener('foo.action', array($listener, 'onFooAction')); @@ -149,11 +152,11 @@ The ``addListener()`` method takes up to three arguments: * A PHP callable that will be notified when an event is thrown that it listens to; -* An optional priority integer (higher equals more important, and therefore +* An optional priority integer (higher equals more important and therefore that the listener will be triggered earlier) that determines when a listener is triggered versus other listeners (defaults to ``0``). If two listeners - have the same priority, they are executed in the order that they were added - to the dispatcher. + have the same priority, they are executed in the order that they were + added to the dispatcher. .. note:: @@ -164,8 +167,8 @@ The ``addListener()`` method takes up to three arguments: a string representing a function, or an array representing an object method or a class method. - So far, you've seen how PHP objects can be registered as listeners. You - can also register PHP `Closures`_ as event listeners:: + So far, you've seen how PHP objects can be registered as listeners. + You can also register PHP `Closures`_ as event listeners:: use Symfony\Component\EventDispatcher\Event; @@ -173,10 +176,10 @@ The ``addListener()`` method takes up to three arguments: // will be executed when the foo.action event is dispatched }); -Once a listener is registered with the dispatcher, it waits until the event is -notified. In the above example, when the ``foo.action`` event is dispatched, -the dispatcher calls the ``AcmeListener::onFooAction`` method and passes the -``Event`` object as the single argument:: +Once a listener is registered with the dispatcher, it waits until the event +is notified. In the above example, when the ``foo.action`` event is dispatched, +the dispatcher calls the ``AcmeListener::onFooAction`` method and passes +the ``Event`` object as the single argument:: use Symfony\Component\EventDispatcher\Event; @@ -190,12 +193,13 @@ the dispatcher calls the ``AcmeListener::onFooAction`` method and passes the } } -In many cases, a special ``Event`` subclass that's specific to the given event -is passed to the listener. This gives the listener access to special -information about the event. Check the documentation or implementation of each -event to determine the exact ``Symfony\Component\EventDispatcher\Event`` -instance that's being passed. For example, the ``kernel.response`` event passes an -instance of ``Symfony\Component\HttpKernel\Event\FilterResponseEvent``:: +In many cases, a special ``Event`` subclass that's specific to the given +event is passed to the listener. This gives the listener access to special +information about the event. Check the documentation or implementation of +each event to determine the exact ``Symfony\Component\EventDispatcher\Event`` +instance that's being passed. For example, the ``kernel.response`` event +passes an instance of +``Symfony\Component\HttpKernel\Event\FilterResponseEvent``:: use Symfony\Component\HttpKernel\Event\FilterResponseEvent; @@ -247,9 +251,9 @@ instance of ``Symfony\Component\HttpKernel\Event\FilterResponseEvent``:: By default, the listeners pass assumes that the event dispatcher's service id is ``event_dispatcher``, that event listeners are tagged with the - ``kernel.event_listener`` tag and that event subscribers are tagged with - the ``kernel.event_subscriber`` tag. You can change these default values - by passing custom values to the constructor of ``RegisterListenersPass``. + ``kernel.event_listener`` tag and that event subscribers are tagged + with the ``kernel.event_subscriber`` tag. You can change these default + values by passing custom values to the constructor of ``RegisterListenersPass``. .. _event_dispatcher-closures-as-listeners: @@ -259,10 +263,10 @@ instance of ``Symfony\Component\HttpKernel\Event\FilterResponseEvent``:: Creating and Dispatching an Event ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In addition to registering listeners with existing events, you can create and -dispatch your own events. This is useful when creating third-party libraries -and also when you want to keep different components of your own system -flexible and decoupled. +In addition to registering listeners with existing events, you can create +and dispatch your own events. This is useful when creating third-party +libraries and also when you want to keep different components of your own +system flexible and decoupled. The Static ``Events`` Class ........................... @@ -335,9 +339,9 @@ Dispatch the Event .................. The :method:`Symfony\\Component\\EventDispatcher\\EventDispatcher::dispatch` -method notifies all listeners of the given event. It takes two arguments: the -name of the event to dispatch and the ``Event`` instance to pass to each -listener of that event:: +method notifies all listeners of the given event. It takes two arguments: +the name of the event to dispatch and the ``Event`` instance to pass to +each listener of that event:: use Acme\StoreBundle\StoreEvents; use Acme\StoreBundle\Order; @@ -351,10 +355,10 @@ listener of that event:: $event = new FilterOrderEvent($order); $dispatcher->dispatch(StoreEvents::STORE_ORDER, $event); -Notice that the special ``FilterOrderEvent`` object is created and passed to -the ``dispatch`` method. Now, any listener to the ``store.order`` event will -receive the ``FilterOrderEvent`` and have access to the ``Order`` object via -the ``getOrder`` method:: +Notice that the special ``FilterOrderEvent`` object is created and passed +to the ``dispatch`` method. Now, any listener to the ``store.order`` event +will receive the ``FilterOrderEvent`` and have access to the ``Order`` object +via the ``getOrder`` method:: // some listener class that's been registered for "store.order" event use Acme\StoreBundle\Event\FilterOrderEvent; @@ -374,8 +378,8 @@ Using Event Subscribers ~~~~~~~~~~~~~~~~~~~~~~~ The most common way to listen to an event is to register an *event listener* -with the dispatcher. This listener can listen to one or more events and is -notified each time those events are dispatched. +with the dispatcher. This listener can listen to one or more events and +is notified each time those events are dispatched. Another way to listen to events is via an *event subscriber*. An event subscriber is a PHP class that's able to tell the dispatcher exactly which @@ -438,13 +442,13 @@ method:: The dispatcher will automatically register the subscriber for each event returned by the ``getSubscribedEvents`` method. This method returns an array -indexed by event names and whose values are either the method name to call or -an array composed of the method name to call and a priority. The example -above shows how to register several listener methods for the same event in -subscriber and also shows how to pass the priority of each listener method. +indexed by event names and whose values are either the method name to call +or an array composed of the method name to call and a priority. The example +above shows how to register several listener methods for the same event +in subscriber and also shows how to pass the priority of each listener method. The higher the priority, the earlier the method is called. In the above example, when the ``kernel.response`` event is triggered, the methods -``onKernelResponsePre``, ``onKernelResponseMid``, and ``onKernelResponsePost`` +``onKernelResponsePre``, ``onKernelResponseMid`` and ``onKernelResponsePost`` are called in that order. .. index:: @@ -456,10 +460,10 @@ Stopping Event Flow/Propagation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In some cases, it may make sense for a listener to prevent any other listeners -from being called. In other words, the listener needs to be able to tell the -dispatcher to stop all propagation of the event to future listeners (i.e. to -not notify any more listeners). This can be accomplished from inside a -listener via the +from being called. In other words, the listener needs to be able to tell +the dispatcher to stop all propagation of the event to future listeners +(i.e. to not notify any more listeners). This can be accomplished from +inside a listener via the :method:`Symfony\\Component\\EventDispatcher\\Event::stopPropagation` method:: use Acme\StoreBundle\Event\FilterOrderEvent; @@ -471,12 +475,12 @@ listener via the $event->stopPropagation(); } -Now, any listeners to ``store.order`` that have not yet been called will *not* -be called. +Now, any listeners to ``store.order`` that have not yet been called will +*not* be called. It is possible to detect if an event was stopped by using the -:method:`Symfony\\Component\\EventDispatcher\\Event::isPropagationStopped` method -which returns a boolean value:: +:method:`Symfony\\Component\\EventDispatcher\\Event::isPropagationStopped` +method which returns a boolean value:: $dispatcher->dispatch('foo.event', $event); if ($event->isPropagationStopped()) { @@ -488,18 +492,18 @@ which returns a boolean value:: .. _event_dispatcher-dispatcher-aware-events: -EventDispatcher aware Events and Listeners +EventDispatcher Aware Events and Listeners ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``EventDispatcher`` always injects a reference to itself in the passed event -object. This means that all listeners have direct access to the +The ``EventDispatcher`` always injects a reference to itself in the passed +event object. This means that all listeners have direct access to the ``EventDispatcher`` object that notified the listener via the passed ``Event`` object's :method:`Symfony\\Component\\EventDispatcher\\Event::getDispatcher` method. This can lead to some advanced applications of the ``EventDispatcher`` including -letting listeners dispatch other events, event chaining or even lazy loading of -more listeners into the dispatcher object. Examples follow: +letting listeners dispatch other events, event chaining or even lazy loading +of more listeners into the dispatcher object. Examples follow: Lazy loading listeners:: @@ -570,8 +574,8 @@ Or setter injection:: } } -Choosing between the two is really a matter of taste. Many tend to prefer the -constructor injection as the objects are fully initialized at construction +Choosing between the two is really a matter of taste. Many tend to prefer +the constructor injection as the objects are fully initialized at construction time. But when you have a long list of dependencies, using setter injection can be the way to go, especially for optional dependencies. @@ -585,17 +589,17 @@ Dispatcher Shortcuts The :method:`EventDispatcher::dispatch ` method always returns an :class:`Symfony\\Component\\EventDispatcher\\Event` -object. This allows for various shortcuts. For example, if one does not need -a custom event object, one can simply rely on a plain -:class:`Symfony\\Component\\EventDispatcher\\Event` object. You do not even need -to pass this to the dispatcher as it will create one by default unless you -specifically pass one:: +object. This allows for various shortcuts. For example, if one does not +need a custom event object, one can simply rely on a plain +:class:`Symfony\\Component\\EventDispatcher\\Event` object. You do not even +need to pass this to the dispatcher as it will create one by default unless +you specifically pass one:: $dispatcher->dispatch('foo.event'); Moreover, the event dispatcher always returns whichever event object that -was dispatched, i.e. either the event that was passed or the event that was -created internally by the dispatcher. This allows for nice shortcuts:: +was dispatched, i.e. either the event that was passed or the event that +was created internally by the dispatcher. This allows for nice shortcuts:: if (!$dispatcher->dispatch('foo.event')->isPropagationStopped()) { // ... @@ -626,8 +630,8 @@ it, the event name is also injected into the to event listeners via the :method:`Symfony\\Component\\EventDispatcher\\Event::getName` method. -The event name, (as with any other data in a custom event object) can be used as -part of the listener's processing logic:: +The event name, (as with any other data in a custom event object) can be +used as part of the listener's processing logic:: use Symfony\Component\EventDispatcher\Event; @@ -642,8 +646,8 @@ part of the listener's processing logic:: Other Dispatchers ----------------- -Besides the commonly used ``EventDispatcher``, the component comes with 2 -other dispatchers: +Besides the commonly used ``EventDispatcher``, the component comes +with 2 other dispatchers: * :doc:`/components/event_dispatcher/container_aware_dispatcher` * :doc:`/components/event_dispatcher/immutable_dispatcher` diff --git a/components/event_dispatcher/traceable_dispatcher.rst b/components/event_dispatcher/traceable_dispatcher.rst index b9c3e79d4d4..70fad87bb03 100644 --- a/components/event_dispatcher/traceable_dispatcher.rst +++ b/components/event_dispatcher/traceable_dispatcher.rst @@ -42,8 +42,8 @@ to register event listeners and dispatch events:: After your application has been processed, you can use the :method:`Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface::getCalledListeners` -method to retrieve an array of event listeners that have been called in your -application. Similarly, the +method to retrieve an array of event listeners that have been called in +your application. Similarly, the :method:`Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface::getNotCalledListeners` method returns an array of event listeners that have not been called:: From 53b4a3d2dfae4857eabb9ce14e8d2d2307ac8d6a Mon Sep 17 00:00:00 2001 From: Peter Dietrich Date: Wed, 22 Jul 2015 20:25:39 +0200 Subject: [PATCH 0363/2667] rename $input to $greetInput Not a good practice to set/abuse a variable that was passed as an argument --- components/console/introduction.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 81229476e8f..7401ad0d44b 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -469,8 +469,8 @@ Calling a command from another one is straightforward:: '--yell' => true, ); - $input = new ArrayInput($arguments); - $returnCode = $command->run($input, $output); + $greetInput = new ArrayInput($arguments); + $returnCode = $command->run($greetInput, $output); // ... } From 10e022d0a2b4558d62e8cdb3f7e3608581ed6e54 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 2 Jul 2015 21:51:47 +0200 Subject: [PATCH 0364/2667] some additional tweaks for the voter cookbook --- cookbook/security/voters.rst | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index 59f40dbc48c..d0378d72507 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -105,8 +105,8 @@ edit a particular object. Here's an example implementation: break; case self::EDIT: - // we assume that our data object has a method getOwner() to - // get the current owner user entity for this data object + // this assumes that the data object has a getOwner() method + // to get the entity of the user who owns this data object if ($user->getId() === $post->getOwner()->getId()) { return true; } @@ -214,9 +214,7 @@ from the authorization checker is called. $authChecker = $this->get('security.authorization_checker'); - if (false === $authChecker->isGranted('view', $post)) { - throw $this->createAccessDeniedException('Unauthorized access!'); - } + $this->denyAccessUnlessGranted('view', $post, 'Unauthorized access!'); return new Response('

'.$post->getName().'

'); } From 50f1f20c14adb5cd66276a93a7da1ceac854383e Mon Sep 17 00:00:00 2001 From: Jeremy Emery Date: Fri, 3 Jul 2015 10:07:43 +0100 Subject: [PATCH 0365/2667] Update http-foundation.rst Just fixed a small grammatical error --- create_framework/http-foundation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_framework/http-foundation.rst b/create_framework/http-foundation.rst index a0e0183ba58..7f92d9e7fb5 100644 --- a/create_framework/http-foundation.rst +++ b/create_framework/http-foundation.rst @@ -221,7 +221,7 @@ Last but not the least, these classes, like every other class in the Symfony code, have been `audited`_ for security issues by an independent company. And being an Open-Source project also means that many other developers around the world have read the code and have already fixed potential security problems. -When was the last you ordered a professional security audit for your home-made +When was the last time you ordered a professional security audit for your home-made framework? Even something as simple as getting the client IP address can be insecure:: From aed00de13a96634b805e92e6aaadb8c8920db04c Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 6 Jul 2015 11:47:53 +0200 Subject: [PATCH 0366/2667] changed headline --- create_framework/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/create_framework/index.rst b/create_framework/index.rst index 10517e1565c..b6a70f5e264 100644 --- a/create_framework/index.rst +++ b/create_framework/index.rst @@ -1,5 +1,5 @@ -Create your PHP Framework -========================= +Create your own PHP Framework +============================= .. toctree:: From 944a795ad866d8d4f73163216a8f06257c494f39 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 6 Jul 2015 12:57:57 +0200 Subject: [PATCH 0367/2667] added composer info --- create_framework/http-foundation.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/create_framework/http-foundation.rst b/create_framework/http-foundation.rst index 7f92d9e7fb5..dd3d4229e91 100644 --- a/create_framework/http-foundation.rst +++ b/create_framework/http-foundation.rst @@ -121,6 +121,18 @@ To use this component, add it as a dependency of the project: Running this command will also automatically download the Symfony HttpFoundation component and install it under the ``vendor/`` directory. +A ``composer.json`` and a ``composer.lock`` file will be generated as well: + +.. code-block:: json + + { + "require": { + "symfony/http-foundation": "^2.7" + } + +The code block shows the content of the ``composer.json`` file (the actual +version may vary). + .. sidebar:: Class Autoloading From ae140026b47b42e530559d1201faff9f0a6967e8 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 23 Jul 2015 11:17:12 +0200 Subject: [PATCH 0368/2667] [#5491] Fix syntax error --- create_framework/http-foundation.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/create_framework/http-foundation.rst b/create_framework/http-foundation.rst index dd3d4229e91..e7d4c41646b 100644 --- a/create_framework/http-foundation.rst +++ b/create_framework/http-foundation.rst @@ -121,19 +121,20 @@ To use this component, add it as a dependency of the project: Running this command will also automatically download the Symfony HttpFoundation component and install it under the ``vendor/`` directory. -A ``composer.json`` and a ``composer.lock`` file will be generated as well: +A ``composer.json`` and a ``composer.lock`` file will be generated as well, +containing the new requirement: .. code-block:: json { "require": { "symfony/http-foundation": "^2.7" + } } The code block shows the content of the ``composer.json`` file (the actual version may vary). - .. sidebar:: Class Autoloading When installing a new dependency, Composer also generates a From b0527d6a58f5e3d798ba158a8962e594802bf1a2 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 6 Jul 2015 14:44:03 +0200 Subject: [PATCH 0369/2667] updated tree --- create_framework/separation-of-concerns.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/create_framework/separation-of-concerns.rst b/create_framework/separation-of-concerns.rst index 5f8384f23b0..d7bb049970c 100644 --- a/create_framework/separation-of-concerns.rst +++ b/create_framework/separation-of-concerns.rst @@ -150,7 +150,8 @@ To sum up, here is the new file layout: example.com ├── composer.json - │ src + ├── composer.lock + ├── src │ ├── app.php │ └── Simplex │ └── Framework.php @@ -160,6 +161,7 @@ To sum up, here is the new file layout: │ └── Model │ └── LeapYear.php ├── vendor + │ └── autoload.php └── web └── front.php From 2437bd98f051f03e48f77a72e4034e9ac7aad2c0 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 23 Jul 2015 11:34:58 +0200 Subject: [PATCH 0370/2667] [#5499] Added versionadded directive --- reference/forms/types/entity.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/reference/forms/types/entity.rst b/reference/forms/types/entity.rst index 007f439efab..5aabfdd5f23 100644 --- a/reference/forms/types/entity.rst +++ b/reference/forms/types/entity.rst @@ -106,6 +106,10 @@ Field Options choice_label ~~~~~~~~~~~~ +.. versionadded:: 2.7 + The ``choice_label`` option was introduced in Symfony 2.7. Prior to Symfony + 2.7, it was called ``property`` (which has the same functionality). + **type**: ``string`` This is the property that should be used for displaying the entities From 5dd9aeae34067173b9fd7a3b5d216078eb740068 Mon Sep 17 00:00:00 2001 From: TrueGit Date: Tue, 7 Jul 2015 15:38:04 -0400 Subject: [PATCH 0371/2667] Fix typo in url for PHPUnit test coverage report In symfony-docs/create_framework/unit-testing.rst, on topic of using PHPUnit to create a test coverage report, it appears the path to the report should be "example.com/cov/src/Simplex/Framework.php.html" instead of "example.com/cov/src_Simplex_Framework.php.html". --- create_framework/unit-testing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_framework/unit-testing.rst b/create_framework/unit-testing.rst index de26e202570..5dcf1ca89ec 100644 --- a/create_framework/unit-testing.rst +++ b/create_framework/unit-testing.rst @@ -178,7 +178,7 @@ coverage feature (you need to enable `XDebug`_ first): $ phpunit --coverage-html=cov/ -Open ``example.com/cov/src_Simplex_Framework.php.html`` in a browser and check +Open ``example.com/cov/src/Simplex/Framework.php.html`` in a browser and check that all the lines for the Framework class are green (it means that they have been visited when the tests were executed). From 81fc69a185afdfccbaa4c74bb8c8d0fd0dd5c260 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 23 Jul 2015 11:43:31 +0200 Subject: [PATCH 0372/2667] bug #5501 Fix typo in url for PHPUnit test coverage report (TrueGit) From a52b159edc6d6a0f937c55cbe12068ed7835e5d3 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 13 Jul 2015 09:48:19 +0200 Subject: [PATCH 0373/2667] Added a note about session data size in PdoSessionHandler --- .../configuration/pdo_session_storage.rst | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/cookbook/configuration/pdo_session_storage.rst b/cookbook/configuration/pdo_session_storage.rst index 03445fadbf1..f36a31803ac 100644 --- a/cookbook/configuration/pdo_session_storage.rst +++ b/cookbook/configuration/pdo_session_storage.rst @@ -7,7 +7,7 @@ How to Use PdoSessionHandler to Store Sessions in the Database The default Symfony session storage writes the session information to files. Most medium to large websites use a database to store the session values instead of files, because databases are easier to use and scale in a -multi webserver environment. +multiple web server environment. Symfony has a built-in solution for database session storage called :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\PdoSessionHandler`. @@ -172,15 +172,18 @@ of your project's data, you can use the connection settings from the '%database_password%', )); -Example SQL Statements ----------------------- +.. _example-sql-statements: + +Preparing the Database to Store Sessions +---------------------------------------- + +Before storing sessions in the database, you must create the table that stores +the information. The following sections contain some examples of the SQL statements +you may use for your specific database engine. MySQL ~~~~~ -The SQL statement for creating the needed database table might look like the -following (MySQL): - .. code-block:: sql CREATE TABLE `session` ( @@ -193,8 +196,6 @@ following (MySQL): PostgreSQL ~~~~~~~~~~ -For PostgreSQL, the statement should look like this: - .. code-block:: sql CREATE TABLE session ( @@ -207,8 +208,6 @@ For PostgreSQL, the statement should look like this: Microsoft SQL Server ~~~~~~~~~~~~~~~~~~~~ -For MSSQL, the statement might look like the following: - .. code-block:: sql CREATE TABLE [dbo].[session]( @@ -225,3 +224,16 @@ For MSSQL, the statement might look like the following: ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] + +.. note:: + + If the session data doesn't fit in the data column, it might get truncated + by the database engine. To make matters worse, when the session data gets + corrupted, PHP ignores the data without giving a warning. + + If the application stores large amounts of session data, this problem can + be solved by increasing the column size (use ``BLOB`` or even ``MEDIUMBLOB``). + When using MySQL as the database engine, you can also enable the `strict SQL mode`_ + to get noticed when such an error happens. + +.. _`strict SQL mode`: https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html From fc9dd41a4677ab42eea22eb165520b51e53c4834 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 23 Jul 2015 12:27:11 +0200 Subject: [PATCH 0374/2667] [#5516] Change note to caution --- cookbook/configuration/pdo_session_storage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/configuration/pdo_session_storage.rst b/cookbook/configuration/pdo_session_storage.rst index f36a31803ac..69d3dcd380b 100644 --- a/cookbook/configuration/pdo_session_storage.rst +++ b/cookbook/configuration/pdo_session_storage.rst @@ -225,7 +225,7 @@ Microsoft SQL Server ) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] -.. note:: +.. caution:: If the session data doesn't fit in the data column, it might get truncated by the database engine. To make matters worse, when the session data gets From a4db2756f205f25c65bdcf77bb57685cd2e8005a Mon Sep 17 00:00:00 2001 From: XitasoChris Date: Mon, 20 Jul 2015 16:52:09 +0200 Subject: [PATCH 0375/2667] Add deprecation notice to "choice_list" option of ChoiceType --- reference/forms/types/choice.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index ba672ebf3b3..e8a22c6c7fc 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -102,6 +102,11 @@ is the item value and the array value is the item's label:: choice_list ~~~~~~~~~~~ +.. caution:: + + The ``choice_list`` option of ChoiceType was deprecated in Symfony 2.7. + You should use ``choices`` or ``choice_loader`` now. + **type**: :class:`Symfony\\Component\\Form\\Extension\\Core\\ChoiceList\\ChoiceListInterface` This is one way of specifying the options to be used for this field. From 8f3ec0c6b2a37fce52184d5384b9889bb80aaba3 Mon Sep 17 00:00:00 2001 From: mojzis Date: Thu, 16 Jul 2015 19:09:23 +0200 Subject: [PATCH 0376/2667] Replace Capifony with Capistrano/symfony --- cookbook/deployment/tools.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cookbook/deployment/tools.rst b/cookbook/deployment/tools.rst index 322154ba9f2..c53ed2fa6a5 100644 --- a/cookbook/deployment/tools.rst +++ b/cookbook/deployment/tools.rst @@ -66,9 +66,10 @@ Using Build Scripts and other Tools There are also tools to help ease the pain of deployment. Some of them have been specifically tailored to the requirements of Symfony. -`Capifony`_ - This Ruby-based tool provides a specialized set of tools on top of - `Capistrano`_, tailored specifically to Symfony projects. +`Capistrano`_ with `Symfony plugin`_ + `Capistrano`_ is a remote server automation and deployment tool written in Ruby. + `Symfony plugin`_ is a plugin to ease Symfony related tasks, inspired by `Capifony`_ + (which works only with Capistrano 2 ) `sf2debpkg`_ Helps you build a native Debian package for your Symfony project. @@ -198,3 +199,5 @@ other potential things like pushing assets to a CDN (see `Common Post-Deployment .. _`bundles that add deployment features`: http://knpbundles.com/search?q=deploy .. _`Memcached`: http://memcached.org/ .. _`Redis`: http://redis.io/ +.. _`Symfony plugin`: https://github.com/capistrano/symfony/ + From 966be4a6caa61cd9f9890336de8cb338dc3b1c5e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 19 Jun 2015 10:25:37 +0200 Subject: [PATCH 0377/2667] Fix doc about deprecations policy --- contributing/code/conventions.rst | 11 +++- cookbook/map.rst.inc | 1 - cookbook/upgrade/deprecation_warnings.rst | 74 ----------------------- cookbook/upgrade/index.rst | 1 - cookbook/upgrade/major_version.rst | 24 ++++++-- 5 files changed, 28 insertions(+), 83 deletions(-) delete mode 100644 cookbook/upgrade/deprecation_warnings.rst diff --git a/contributing/code/conventions.rst b/contributing/code/conventions.rst index e48eebd414e..4e698f5a182 100644 --- a/contributing/code/conventions.rst +++ b/contributing/code/conventions.rst @@ -92,7 +92,7 @@ A feature is marked as deprecated by adding a ``@deprecated`` phpdoc to relevant classes, methods, properties, ...:: /** - * @deprecated Deprecated since version 2.X, to be removed in 2.Y. Use XXX instead. + * @deprecated Deprecated since version 2.8, to be removed in 3.0. Use XXX instead. */ The deprecation message should indicate the version when the class/method was @@ -103,4 +103,11 @@ A PHP ``E_USER_DEPRECATED`` error must also be triggered to help people with the migration starting one or two minor versions before the version where the feature will be removed (depending on the criticality of the removal):: - trigger_error('XXX() is deprecated since version 2.X and will be removed in 2.Y. Use XXX instead.', E_USER_DEPRECATED); + @trigger_error('XXX() is deprecated since version 2.8 and will be removed in 3.0. Use XXX instead.', E_USER_DEPRECATED); + +Without the `@-silencing operator`_, users would need to opt-out from deprecation +notices. Silencing swaps this behavior and allows users to opt-in when they are +ready to cope with them (by adding a custom error handler like the one used by +the Web Debug Toolbar or by the PHPUnit bridge). + +.. _`@-silencing operator`: https://php.net/manual/en/language.operators.errorcontrol.php diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 691c9a71d73..4316f0de726 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -224,7 +224,6 @@ * :doc:`/cookbook/upgrade/patch_version` * :doc:`/cookbook/upgrade/minor_version` * :doc:`/cookbook/upgrade/major_version` - * :doc:`/cookbook/upgrade/deprecation_warnings` * :doc:`/cookbook/validation/index` diff --git a/cookbook/upgrade/deprecation_warnings.rst b/cookbook/upgrade/deprecation_warnings.rst deleted file mode 100644 index 12e6c21c7e9..00000000000 --- a/cookbook/upgrade/deprecation_warnings.rst +++ /dev/null @@ -1,74 +0,0 @@ -What do these "XXX is deprecated " E_USER_DEPRECATED Warnings mean? -=================================================================== - -Starting in Symfony 2.7, if you use a deprecated class, function or option, -Symfony triggers an ``E_USER_DEPRECATED`` error. Internally, that looks something -like this:: - - trigger_error( - 'The fooABC method is deprecated since version 2.4 and will be removed in 3.0.', - E_USER_DEPRECATED - ); - -This is great, because you can check your logs to know what needs to change -before you upgrade. In the Symfony Framework, the number of deprecated calls -shows up in the web debug toolbar. And if you install the `phpunit-bridge`_, -you can get a report of deprecated calls after running your tests. - -How can I Silence the Warnings? -------------------------------- - -As useful as these are, you don't want them to show up while developing and -you may also want to silence them on production to avoid filling up your -error logs. - -In the Symfony Framework -~~~~~~~~~~~~~~~~~~~~~~~~ - -In the Symfony Framework, ``~E_USER_DEPRECATED`` is added to ``app/bootstrap.php.cache`` -automatically, but you need at least version 2.3.14 or 3.0.21 of the -`SensioDistributionBundle`_. So, you may need to upgrade: - -.. code-block:: bash - - $ composer update sensio/distribution-bundle - -Once you've updated, the ``bootstrap.php.cache`` file is rebuilt automatically. -At the top, you should see a line adding ``~E_USER_DEPRECATED``. - -Outside of the Symfony Framework -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To do that, add ``~E_USER_DEPRECATED`` to your ``error_reporting`` -setting in ``php.ini``: - -.. code-block:: ini - - ; before - error_reporting = E_ALL - ; after - error_reporting = E_ALL & ~E_USER_DEPRECATED - -Alternatively, you can set this directly in bootstrap of your project:: - - error_reporting(error_reporting() & ~E_USER_DEPRECATED); - -How can I Fix the Warnings? ---------------------------- - -Of course ultimately, you want to stop using the deprecated functionality. -Sometimes, this is easy: the warning might tell you exactly what to change. - -But other times, the warning might be unclear: a setting somewhere might -cause a class deeper to trigger the warning. In this case, Symfony does its -best to give a clear message, but you may need to research that warning further. - -And sometimes, the warning may come from a third-party library or bundle -that you're using. If that's true, there's a good chance that those deprecations -have already been updated. In that case, upgrade the library to fix them. - -Once all the deprecation warnings are gone, you can upgrade with a lot -more confidence. - -.. _`phpunit-bridge`: https://github.com/symfony/phpunit-bridge -.. _`SensioDistributionBundle`: https://github.com/sensiolabs/SensioDistributionBundle diff --git a/cookbook/upgrade/index.rst b/cookbook/upgrade/index.rst index 3a37aa78a61..b943f2ae32a 100644 --- a/cookbook/upgrade/index.rst +++ b/cookbook/upgrade/index.rst @@ -16,4 +16,3 @@ There are three types of upgrades, all needing a little different preparation: /cookbook/upgrade/patch_version /cookbook/upgrade/minor_version /cookbook/upgrade/major_version - /cookbook/upgrade/deprecation_warnings diff --git a/cookbook/upgrade/major_version.rst b/cookbook/upgrade/major_version.rst index 0c5107b6fed..8ff9ff51f6b 100644 --- a/cookbook/upgrade/major_version.rst +++ b/cookbook/upgrade/major_version.rst @@ -26,7 +26,7 @@ There are a couple of steps to upgrading a major version: During the lifecycle of a major release, new features are added and method signatures and public API usages are changed. However, :doc:`minor versions ` should not contain any -backwards compatibility changes. To accomplish this, the "old" (e.g. functions, +backwards incompatible changes. To accomplish this, the "old" (e.g. functions, classes, etc) code still works, but is marked as *deprecated*, indicating that it will be removed/changed in the future and that you should stop using it. @@ -35,13 +35,27 @@ functionality are removed. So, as long as you've updated your code to stop using these deprecated features in the last version before the major (e.g. 2.8.*), you should be able to upgrade without a problem. -To help you with this, the last minor releases will trigger deprecated notices. -For example, 2.7 and 2.8 trigger deprecated notices. When visiting your -application in the :doc:`dev environment ` +To help you with this, deprecation notices are triggered whenever you end up +using a deprecated feature. When visiting your application in the +:doc:`dev environment ` in your browser, these notices are shown in the web dev toolbar: .. image:: /images/cookbook/deprecations-in-profiler.png +Of course ultimately, you want to stop using the deprecated functionality. +Sometimes, this is easy: the warning might tell you exactly what to change. + +But other times, the warning might be unclear: a setting somewhere might +cause a class deeper to trigger the warning. In this case, Symfony does its +best to give a clear message, but you may need to research that warning further. + +And sometimes, the warning may come from a third-party library or bundle +that you're using. If that's true, there's a good chance that those deprecations +have already been updated. In that case, upgrade the library to fix them. + +Once all the deprecation warnings are gone, you can upgrade with a lot +more confidence. + Deprecations in PHPUnit ~~~~~~~~~~~~~~~~~~~~~~~ @@ -52,7 +66,7 @@ To make sure this doesn't happen, you can install the PHPUnit bridge: .. code-block:: bash - $ composer require symfony/phpunit-bridge + $ composer require --dev symfony/phpunit-bridge Now, your tests execute normally and a nice summary of the deprecation notices is displayed at the end of the test report: From 40eb0bd77d541df88cd2e98a90212316407022e6 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 21 Jul 2015 13:18:00 +0200 Subject: [PATCH 0378/2667] Misc. improvements in the Console component introduction --- components/console/introduction.rst | 47 +++++++++++++++++------------ 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 81229476e8f..ad772247b62 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -140,6 +140,9 @@ output. For example:: // white text on a red background $output->writeln('foo'); +The closing tag can be replaced by ````, which revokes all formatting options +established by the last opened tag. + It is possible to define your own styles using the class :class:`Symfony\\Component\\Console\\Formatter\\OutputFormatterStyle`:: @@ -148,23 +151,27 @@ It is possible to define your own styles using the class // ... $style = new OutputFormatterStyle('red', 'yellow', array('bold', 'blink')); $output->getFormatter()->setStyle('fire', $style); - $output->writeln('foo'); + $output->writeln('foo'); Available foreground and background colors are: ``black``, ``red``, ``green``, ``yellow``, ``blue``, ``magenta``, ``cyan`` and ``white``. -And available options are: ``bold``, ``underscore``, ``blink``, ``reverse`` and ``conceal``. +And available options are: ``bold``, ``underscore``, ``blink``, ``reverse`` +(enables the "reverse video" mode where the background and foreground colors +are swapped) and ``conceal`` (sets the foreground color to transparent, making +the typed text invisible - although it can be selected and copied; this option is +commonly used when asking the user to type sensitive information). You can also set these colors and options inside the tagname:: // green text - $output->writeln('foo'); + $output->writeln('foo'); // black text on a cyan background - $output->writeln('foo'); + $output->writeln('foo'); // bold text on a yellow background - $output->writeln('foo'); + $output->writeln('foo'); Verbosity Levels ~~~~~~~~~~~~~~~~ @@ -261,15 +268,15 @@ You can access the ``names`` argument as an array:: $text .= ' '.implode(', ', $names); } -There are 3 argument variants you can use: +There are three argument variants you can use: -=========================== =============================================================================================================== +=========================== =========================================================================================================== Mode Value -=========================== =============================================================================================================== -InputArgument::REQUIRED The argument is required -InputArgument::OPTIONAL The argument is optional and therefore can be omitted -InputArgument::IS_ARRAY The argument can contain an indefinite number of arguments and must be used at the end of the argument list -=========================== =============================================================================================================== +=========================== =========================================================================================================== +``InputArgument::REQUIRED`` The argument is required +``InputArgument::OPTIONAL`` The argument is optional and therefore can be omitted +``InputArgument::IS_ARRAY`` The argument can contain an indefinite number of arguments and must be used at the end of the argument list +=========================== =========================================================================================================== You can combine ``IS_ARRAY`` with ``REQUIRED`` and ``OPTIONAL`` like this:: @@ -342,14 +349,14 @@ will work: There are 4 option variants you can use: -=========================== ===================================================================================== -Option Value -=========================== ===================================================================================== -InputOption::VALUE_IS_ARRAY This option accepts multiple values (e.g. ``--dir=/foo --dir=/bar``) -InputOption::VALUE_NONE Do not accept input for this option (e.g. ``--yell``) -InputOption::VALUE_REQUIRED This value is required (e.g. ``--iterations=5``), the option itself is still optional -InputOption::VALUE_OPTIONAL This option may or may not have a value (e.g. ``--yell`` or ``--yell=loud``) -=========================== ===================================================================================== +=============================== ===================================================================================== +Option Value +=============================== ===================================================================================== +``InputOption::VALUE_IS_ARRAY`` This option accepts multiple values (e.g. ``--dir=/foo --dir=/bar``) +``InputOption::VALUE_NONE`` Do not accept input for this option (e.g. ``--yell``) +``InputOption::VALUE_REQUIRED`` This value is required (e.g. ``--iterations=5``), the option itself is still optional +``InputOption::VALUE_OPTIONAL`` This option may or may not have a value (e.g. ``--yell`` or ``--yell=loud``) +=============================== ===================================================================================== You can combine ``VALUE_IS_ARRAY`` with ``VALUE_REQUIRED`` or ``VALUE_OPTIONAL`` like this: From 65dca624e47bda6c4559b7d37ffe23a51dd8d6f8 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 6 Jul 2015 14:25:02 +0200 Subject: [PATCH 0379/2667] updated tree for front controller --- create_framework/front-controller.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/create_framework/front-controller.rst b/create_framework/front-controller.rst index 90e7e69dbb0..c132580f9c4 100644 --- a/create_framework/front-controller.rst +++ b/create_framework/front-controller.rst @@ -146,11 +146,13 @@ web root directory: example.com ├── composer.json - │ src + ├── composer.lock + ├── src │ └── pages │ ├── hello.php │ └── bye.php ├── vendor + │ └── autoload.php └── web └── front.php From a6e9a10873eca5e97a77abab95622aa58e304787 Mon Sep 17 00:00:00 2001 From: Javier Spagnoletti Date: Thu, 23 Jul 2015 16:27:20 -0300 Subject: [PATCH 0380/2667] [2.3] [Contributing] Added note about empty returns | Q | A | ------------- | --- | Doc fix? | no | New docs? | no | Applies to | | Fixed tickets | --- contributing/code/standards.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index 2168129d7fa..97491888bc8 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -112,6 +112,9 @@ Structure * Add a blank line before ``return`` statements, unless the return is alone inside a statement-group (like an ``if`` statement); +* Use just ``return;`` instead of ``return null;`` when a function must return void + early; + * Use braces to indicate control structure body regardless of the number of statements it contains; From 39b054278c9f02a57164331378202ade79c3b255 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 25 Jul 2015 11:00:33 +0200 Subject: [PATCH 0381/2667] fix max line length --- contributing/code/standards.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index 97491888bc8..7d6d9f09987 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -112,8 +112,8 @@ Structure * Add a blank line before ``return`` statements, unless the return is alone inside a statement-group (like an ``if`` statement); -* Use just ``return;`` instead of ``return null;`` when a function must return void - early; +* Use just ``return;`` instead of ``return null;`` when a function must return + void early; * Use braces to indicate control structure body regardless of the number of statements it contains; From bc880d09d7e31b15f8f9386ef9a6e8413caf5546 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 19 Jul 2015 14:45:32 +0200 Subject: [PATCH 0382/2667] add missing versionadded directive --- cookbook/email/email.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cookbook/email/email.rst b/cookbook/email/email.rst index 361ada048ce..6b205fb5770 100644 --- a/cookbook/email/email.rst +++ b/cookbook/email/email.rst @@ -138,6 +138,11 @@ template might look something like this: {# Makes an absolute URL to the /images/logo.png file #} Date: Tue, 28 Jul 2015 12:55:20 +0100 Subject: [PATCH 0383/2667] Change Sql Field name because it's reserved --- book/from_flat_php_to_symfony2.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/from_flat_php_to_symfony2.rst b/book/from_flat_php_to_symfony2.rst index 5938ec8323a..67c39c5a0e1 100644 --- a/book/from_flat_php_to_symfony2.rst +++ b/book/from_flat_php_to_symfony2.rst @@ -266,7 +266,7 @@ an individual blog result based on a given id:: $link = open_database_connection(); $id = intval($id); - $query = 'SELECT date, title, body FROM post WHERE id = '.$id; + $query = 'SELECT created_date, title, body FROM post WHERE id = '.$id; $result = mysql_query($query); $row = mysql_fetch_assoc($result); @@ -297,7 +297,7 @@ the individual blog post:

-
+
From a9d75c69453b817170c2f9465028105bd0e9c848 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 15 Jul 2015 20:20:06 +0900 Subject: [PATCH 0384/2667] Fix code --- create_framework/templating.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/create_framework/templating.rst b/create_framework/templating.rst index 43e77f92623..987a9ea26d1 100644 --- a/create_framework/templating.rst +++ b/create_framework/templating.rst @@ -41,7 +41,7 @@ rendered:: function render_template($request) { - extract($request->attributes->all(), EXTR_SKIP); + extract($request->attributes->all()); ob_start(); include sprintf(__DIR__.'/../src/pages/%s.php', $_route); @@ -110,7 +110,7 @@ Here is the updated and improved version of our framework:: function render_template($request) { - extract($request->attributes->all(), EXTR_SKIP); + extract($request->attributes->all()); ob_start(); include sprintf(__DIR__.'/../src/pages/%s.php', $_route); From dae28115f5b9395e7a28c0ee125c4f755efeeb9a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 24 May 2015 12:32:57 -0700 Subject: [PATCH 0385/2667] Fix missing note about debug.dump_destination --- components/var_dumper/introduction.rst | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/components/var_dumper/introduction.rst b/components/var_dumper/introduction.rst index 05eff3e8849..086695c471c 100644 --- a/components/var_dumper/introduction.rst +++ b/components/var_dumper/introduction.rst @@ -104,6 +104,11 @@ original value. You can configure the limits in terms of: * maximum number of items to dump, * maximum string length before truncation. +Since dumping into the toolbar is not always possible - e.g. when working on a +JSON API - you can have an alternate output destination for dumps. This is +configurable with the ``debug.dump_destination`` option, that you can typically +set to ``php://stderr``. + .. configuration-block:: .. code-block:: yaml @@ -111,6 +116,7 @@ original value. You can configure the limits in terms of: debug: max_items: 250 max_string_length: -1 + dump_destination: ~ .. code-block:: xml @@ -119,9 +125,17 @@ original value. You can configure the limits in terms of: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/debug http://symfony.com/schema/dic/debug/debug-1.0.xsd"> - +
+ .. code-block:: php + + $container->loadFromExtension('debug', array( + 'max_items' => 250, + 'max_string_length' => -1, + 'dump_destination' => null, + )); + Dump Examples and Output ------------------------ From 0134123dd5c54452a5365321fc60c1533454b0ea Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 28 Jul 2015 16:19:16 +0200 Subject: [PATCH 0386/2667] [#5567] Renamed `created_date` to `created_at` This seems to be more common. --- book/from_flat_php_to_symfony2.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/from_flat_php_to_symfony2.rst b/book/from_flat_php_to_symfony2.rst index 67c39c5a0e1..382a656dab9 100644 --- a/book/from_flat_php_to_symfony2.rst +++ b/book/from_flat_php_to_symfony2.rst @@ -266,7 +266,7 @@ an individual blog result based on a given id:: $link = open_database_connection(); $id = intval($id); - $query = 'SELECT created_date, title, body FROM post WHERE id = '.$id; + $query = 'SELECT created_at, title, body FROM post WHERE id = '.$id; $result = mysql_query($query); $row = mysql_fetch_assoc($result); @@ -297,7 +297,7 @@ the individual blog post:

-
+
From 43e06c30050f94e059b5b15fba8dc865672ede77 Mon Sep 17 00:00:00 2001 From: Nicolas Dewez Date: Thu, 23 Jul 2015 13:53:47 +0200 Subject: [PATCH 0387/2667] Fix typo Esi in part create framework --- create_framework/http-kernel-httpkernelinterface.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/create_framework/http-kernel-httpkernelinterface.rst b/create_framework/http-kernel-httpkernelinterface.rst index e0a07731e36..6f8d6a8e994 100644 --- a/create_framework/http-kernel-httpkernelinterface.rst +++ b/create_framework/http-kernel-httpkernelinterface.rst @@ -153,7 +153,7 @@ sub-requests to convert them to their proper content:: $framework = new HttpKernel\HttpCache\HttpCache( $framework, new HttpKernel\HttpCache\Store(__DIR__.'/../cache'), - new HttpKernel\HttpCache\ESI() + new HttpKernel\HttpCache\Esi() ); .. note:: @@ -166,7 +166,7 @@ When using complex HTTP caching strategies and/or many ESI include tags, it can be hard to understand why and when a resource should be cached or not. To ease debugging, you can enable the debug mode:: - $framework = new HttpCache($framework, new Store(__DIR__.'/../cache'), new ESI(), array('debug' => true)); + $framework = new HttpCache($framework, new Store(__DIR__.'/../cache'), new Esi(), array('debug' => true)); The debug mode adds a ``X-Symfony-Cache`` header to each response that describes what the cache layer did: From 0d283069a5a5b3f3c496d9fee133fae36510144a Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 28 Jul 2015 17:07:04 +0200 Subject: [PATCH 0388/2667] Fixed issues discovered by the human reviewers --- components/class_loader/class_loader.rst | 6 +++--- components/config/resources.rst | 5 ++--- components/dependency_injection/compilation.rst | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/components/class_loader/class_loader.rst b/components/class_loader/class_loader.rst index 26be9882909..9e477795c10 100644 --- a/components/class_loader/class_loader.rst +++ b/components/class_loader/class_loader.rst @@ -42,9 +42,9 @@ is straightforward:: The autoloader is automatically registered in a Symfony application (see ``app/autoload.php``). -Use the :method:`Symfony\\Component\\ClassLoader\\ClassLoader::addPrefix` -or :method:`Symfony\\Component\\ClassLoader\\ClassLoader::addPrefixes` methods -to register your classes:: +Use :method:`Symfony\\Component\\ClassLoader\\ClassLoader::addPrefix` or +:method:`Symfony\\Component\\ClassLoader\\ClassLoader::addPrefixes` to register +your classes:: // register a single namespaces $loader->addPrefix('Symfony', __DIR__.'/vendor/symfony/symfony/src'); diff --git a/components/config/resources.rst b/components/config/resources.rst index a7905e0747d..97924703158 100644 --- a/components/config/resources.rst +++ b/components/config/resources.rst @@ -14,9 +14,8 @@ Loading Resources Locating Resources ------------------ -Loading the configuration normally starts with a search for resources – -in most cases: files. This can be done with the -:class:`Symfony\\Component\\Config\\FileLocator`:: +Loading the configuration normally starts with a search for resources, mostly +files. This can be done with the :class:`Symfony\\Component\\Config\\FileLocator`:: use Symfony\Component\Config\FileLocator; diff --git a/components/dependency_injection/compilation.rst b/components/dependency_injection/compilation.rst index 68ef2693883..388815f3b6c 100644 --- a/components/dependency_injection/compilation.rst +++ b/components/dependency_injection/compilation.rst @@ -99,7 +99,7 @@ The Extension must specify a ``getAlias`` method to implement the interface:: } } -For YAML configuration files specifying the alias for the Extension as a +For YAML configuration files specifying the alias for the extension as a key will mean that those values are passed to the Extension's ``load`` method: .. code-block:: yaml From c14f4daaf6a0064c29b347a7b2cea81f180ff1e5 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 28 Jul 2015 17:34:39 +0200 Subject: [PATCH 0389/2667] Remove merge conflict --- components/dependency_injection/factories.rst | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/components/dependency_injection/factories.rst b/components/dependency_injection/factories.rst index 2cd7f08b6ec..ce5d2bcb49c 100644 --- a/components/dependency_injection/factories.rst +++ b/components/dependency_injection/factories.rst @@ -33,13 +33,8 @@ object:: } To make the ``NewsletterManager`` object available as a service, you can -<<<<<<< HEAD configure the service container to use the ``NewsletterFactory::createNewsletterManager()`` factory method: -======= -configure the service container to use the ``NewsletterManagerFactory`` -factory class: ->>>>>>> 2.3 .. configuration-block:: @@ -82,18 +77,10 @@ factory class: the configured class name may be used by compiler passes and therefore should be set to a sensible value. -<<<<<<< HEAD Now, the method will be called statically. If the factory class itself should be instantiated and the resulting object's method called, configure the factory itself as a service. In this case, the method (e.g. get) should be changed to be non-static. -======= -When you specify the class to use for the factory (via ``factory_class``) -the method will be called statically. If the factory itself should be instantiated -and the resulting object's method called, configure the factory itself -as a service. In this case, the method (e.g. ``createNewsletterManager``) -should be changed to be non-static: ->>>>>>> 2.3 .. configuration-block:: @@ -130,18 +117,12 @@ should be changed to be non-static: // ... $container->register('newsletter_manager.factory', 'NewsletterManagerFactory'); -<<<<<<< HEAD $newsletterManager = new Definition(); $newsletterManager->setFactory(array( new Reference('newsletter_manager.factory'), 'createNewsletterManager' )); $container->setDefinition('newsletter_manager', $newsletterManager); -======= - The factory service is specified by its id name and not a reference - to the service itself. So, you do not need to use the ``@`` syntax for - this in YAML configurations. ->>>>>>> 2.3 Passing Arguments to the Factory Method --------------------------------------- From 41c7059e6d5f5be3590672d8cd9b0f8f1400e9e8 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 28 Jul 2015 17:38:33 +0200 Subject: [PATCH 0390/2667] Fixed another merge conflict --- components/event_dispatcher/introduction.rst | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/components/event_dispatcher/introduction.rst b/components/event_dispatcher/introduction.rst index e1d2bc0d9a4..23bb2ab3193 100644 --- a/components/event_dispatcher/introduction.rst +++ b/components/event_dispatcher/introduction.rst @@ -632,13 +632,8 @@ Event Name Introspection Before Symfony 2.4, the event name and the event dispatcher had to be requested from the ``Event`` instance. These methods are now deprecated. -<<<<<<< HEAD -The ``EventDispatcher`` instance, as well as the name of the event that is -dispatched, are passed as arguments to the listener:: -======= -The event name, (as with any other data in a custom event object) can be -used as part of the listener's processing logic:: ->>>>>>> 2.3 +The ``EventDispatcher`` instance, as well as the name of the event that +is dispatched, are passed as arguments to the listener:: use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\EventDispatcherInterface; From e43c92738f225299616a381a9de39449b74c6142 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 28 Jul 2015 12:34:03 -0400 Subject: [PATCH 0391/2667] [#5413] Backporting changes to 2.3 --- contributing/code/conventions.rst | 11 +++++++++-- cookbook/upgrade/major_version.rst | 24 +++++++++++++++++++----- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/contributing/code/conventions.rst b/contributing/code/conventions.rst index e48eebd414e..4e698f5a182 100644 --- a/contributing/code/conventions.rst +++ b/contributing/code/conventions.rst @@ -92,7 +92,7 @@ A feature is marked as deprecated by adding a ``@deprecated`` phpdoc to relevant classes, methods, properties, ...:: /** - * @deprecated Deprecated since version 2.X, to be removed in 2.Y. Use XXX instead. + * @deprecated Deprecated since version 2.8, to be removed in 3.0. Use XXX instead. */ The deprecation message should indicate the version when the class/method was @@ -103,4 +103,11 @@ A PHP ``E_USER_DEPRECATED`` error must also be triggered to help people with the migration starting one or two minor versions before the version where the feature will be removed (depending on the criticality of the removal):: - trigger_error('XXX() is deprecated since version 2.X and will be removed in 2.Y. Use XXX instead.', E_USER_DEPRECATED); + @trigger_error('XXX() is deprecated since version 2.8 and will be removed in 3.0. Use XXX instead.', E_USER_DEPRECATED); + +Without the `@-silencing operator`_, users would need to opt-out from deprecation +notices. Silencing swaps this behavior and allows users to opt-in when they are +ready to cope with them (by adding a custom error handler like the one used by +the Web Debug Toolbar or by the PHPUnit bridge). + +.. _`@-silencing operator`: https://php.net/manual/en/language.operators.errorcontrol.php diff --git a/cookbook/upgrade/major_version.rst b/cookbook/upgrade/major_version.rst index 60e35db0083..d83aa966893 100644 --- a/cookbook/upgrade/major_version.rst +++ b/cookbook/upgrade/major_version.rst @@ -26,7 +26,7 @@ There are a couple of steps to upgrading a major version: During the lifecycle of a major release, new features are added and method signatures and public API usages are changed. However, :doc:`minor versions ` should not contain any -backwards compatibility changes. To accomplish this, the "old" (e.g. functions, +backwards incompatible changes. To accomplish this, the "old" (e.g. functions, classes, etc) code still works, but is marked as *deprecated*, indicating that it will be removed/changed in the future and that you should stop using it. @@ -35,13 +35,27 @@ functionality are removed. So, as long as you've updated your code to stop using these deprecated features in the last version before the major (e.g. 2.8.*), you should be able to upgrade without a problem. -To help you with this, the last minor releases will trigger deprecated notices. -For example, 2.7 and 2.8 trigger deprecated notices. When visiting your -application in the :doc:`dev environment ` +To help you with this, deprecation notices are triggered whenever you end up +using a deprecated feature. When visiting your application in the +:doc:`dev environment ` in your browser, these notices are shown in the web dev toolbar: .. image:: /images/cookbook/deprecations-in-profiler.png +Of course ultimately, you want to stop using the deprecated functionality. +Sometimes, this is easy: the warning might tell you exactly what to change. + +But other times, the warning might be unclear: a setting somewhere might +cause a class deeper to trigger the warning. In this case, Symfony does its +best to give a clear message, but you may need to research that warning further. + +And sometimes, the warning may come from a third-party library or bundle +that you're using. If that's true, there's a good chance that those deprecations +have already been updated. In that case, upgrade the library to fix them. + +Once all the deprecation warnings are gone, you can upgrade with a lot +more confidence. + Deprecations in PHPUnit ~~~~~~~~~~~~~~~~~~~~~~~ @@ -52,7 +66,7 @@ To make sure this doesn't happen, you can install the PHPUnit bridge: .. code-block:: bash - $ composer require symfony/phpunit-bridge + $ composer require --dev symfony/phpunit-bridge Now, your tests execute normally and a nice summary of the deprecation notices is displayed at the end of the test report: From 9d6db3e8c761b0b06297cb62c6b780f941d8f8bf Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 13 Jun 2015 17:57:57 +0200 Subject: [PATCH 0392/2667] Fix Major upgrade article for 2.7.1 changes --- cookbook/upgrade/major_version.rst | 40 ++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/cookbook/upgrade/major_version.rst b/cookbook/upgrade/major_version.rst index d83aa966893..86dd434c907 100644 --- a/cookbook/upgrade/major_version.rst +++ b/cookbook/upgrade/major_version.rst @@ -59,17 +59,17 @@ more confidence. Deprecations in PHPUnit ~~~~~~~~~~~~~~~~~~~~~~~ -By default, PHPUnit will handle deprecation notices as real errors. This means -that all tests are aborted because it uses a BC layer. +When you run your tests using PHPUnit, no deprecation notices are shown. +To help you here, Symfony provides a PHPUnit bridge. This bridge will show +you a nice summary of all deprecation notices at the end of the test report. -To make sure this doesn't happen, you can install the PHPUnit bridge: +All you need to do is install the PHPUnit bridge: .. code-block:: bash $ composer require --dev symfony/phpunit-bridge -Now, your tests execute normally and a nice summary of the deprecation notices -is displayed at the end of the test report: +Now, you can start fixing the notices: .. code-block:: text @@ -87,12 +87,36 @@ is displayed at the end of the test report: 2x in PageAdminTest::testPageList from Symfony\Cmf\SimpleCmsBundle\Tests\WebTest\Admin 1x in PageAdminTest::testPageEdit from Symfony\Cmf\SimpleCmsBundle\Tests\WebTest\Admin +Once you fixed them all, the command ends with ``0`` (success) and you're +done! + +.. sidebar:: Using the Weak Deprecations Mode + + Sometimes, you can't fix all deprecations (e.g. something was deprecated + in 2.6 and you still need to support 2.3). In these cases, you can still + use the bridge to fix as many deprecations as possible and then switch + to the weak test mode to make your tests pass again. You can do this by + using the ``SYMFONY_DEPRECATIONS_HELPER`` env variable: + + .. code-block:: xml + + + + + + + + + + + (you can also execute the command like ``SYMFONY_DEPRECATIONS_HELPER=weak phpunit``). + .. _upgrade-major-symfony-composer: 2) Update to the New Major Version via Composer ----------------------------------------------- -If your code is deprecation free, you can update the Symfony library via +Once your code is deprecation free, you can update the Symfony library via Composer by modifying your ``composer.json`` file: .. code-block:: json @@ -103,14 +127,14 @@ Composer by modifying your ``composer.json`` file: "require": { "symfony/symfony": "3.0.*", }, - "...": "...", + "...": "..." } Next, use Composer to download new versions of the libraries: .. code-block:: bash - $ composer update symfony/symfony + $ composer update --with-dependencies symfony/symfony .. include:: /cookbook/upgrade/_update_dep_errors.rst.inc From c2f39b4034d34f493b9a53d033bbaf193b7ceafa Mon Sep 17 00:00:00 2001 From: Wouter J Date: Mon, 25 May 2015 09:43:34 +0200 Subject: [PATCH 0393/2667] Some fixes for bundle best practices --- cookbook/bundles/best_practices.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cookbook/bundles/best_practices.rst b/cookbook/bundles/best_practices.rst index 02622aab9fd..58f01c74366 100644 --- a/cookbook/bundles/best_practices.rst +++ b/cookbook/bundles/best_practices.rst @@ -146,7 +146,7 @@ class name is ``Acme\BlogBundle\Controller\ContentController``. All classes and files must follow the :doc:`Symfony coding standards `. Some classes should be seen as facades and should be as short as possible, like -Commands, Helpers, Listeners, and Controllers. +Commands, Helpers, Listeners and Controllers. Classes that connect to the event dispatcher should be suffixed with ``Listener``. @@ -159,7 +159,7 @@ Vendors A bundle must not embed third-party PHP libraries. It should rely on the standard Symfony autoloading instead. -A bundle should not embed third-party libraries written in JavaScript, CSS, or +A bundle should not embed third-party libraries written in JavaScript, CSS or any other language. Tests @@ -175,6 +175,7 @@ the ``Tests/`` directory. Tests should follow the following principles: * The tests should cover at least 95% of the code base. .. note:: + A test suite must not contain ``AllTests.php`` scripts, but must rely on the existence of a ``phpunit.xml.dist`` file. From 5f91e6c97a00c788b1b4435358c77000938e59a7 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Tue, 28 Jul 2015 19:22:26 +0200 Subject: [PATCH 0394/2667] Some small fixes for upload files article --- cookbook/controller/upload_file.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cookbook/controller/upload_file.rst b/cookbook/controller/upload_file.rst index dccd5676773..e61de2a9e05 100644 --- a/cookbook/controller/upload_file.rst +++ b/cookbook/controller/upload_file.rst @@ -122,7 +122,7 @@ Finally, you need to update the code of the controller that handles the form:: if ($form->isValid()) { // $file stores the uploaded PDF file /** @var Symfony\Component\HttpFoundation\File\UploadedFile $file */ - $file = $product->getBrochure() + $file = $product->getBrochure(); // Generate a unique name for the file before saving it $fileName = md5(uniqid()).'.'.$file->guessExtension(); @@ -135,13 +135,13 @@ Finally, you need to update the code of the controller that handles the form:: // instead of its contents $product->setBrochure($filename); - // persist the $product variable or any other work... + // ... persist the $product variable or any other work return $this->redirect($this->generateUrl('app_product_list')); } return $this->render('product/new.html.twig', array( - 'form' => $form->createView() + 'form' => $form->createView(), )); } } @@ -150,10 +150,10 @@ There are some important things to consider in the code of the above controller: #. When the form is uploaded, the ``brochure`` property contains the whole PDF file contents. Since this property stores just the file name, you must set - its new value before persisting the changes of the entity. + its new value before persisting the changes of the entity; #. In Symfony applications, uploaded files are objects of the :class:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile` class, which - provides methods for the most common operations when dealing with uploaded files. + provides methods for the most common operations when dealing with uploaded files; #. A well-known security best practice is to never trust the input provided by users. This also applies to the files uploaded by your visitors. The ``Uploaded`` class provides methods to get the original file extension (:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getExtension`), @@ -162,7 +162,7 @@ There are some important things to consider in the code of the above controller: However, they are considered *not safe* because a malicious user could tamper that information. That's why it's always better to generate a unique name and use the :method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::guessExtension` - method to let Symfony guess the right extension according to the file MIME type. + method to let Symfony guess the right extension according to the file MIME type; #. The ``UploadedFile`` class also provides a :method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::move` method to store the file in its intended directory. Defining this directory path as an application configuration option is considered a good practice that From 5a942f816c080f22478db722f5f31c9593bd3324 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 27 Jul 2015 16:51:16 +0200 Subject: [PATCH 0395/2667] Improve and simplify the contributing instructions about tests --- contributing/code/tests.rst | 89 +++++++++---------------------------- 1 file changed, 22 insertions(+), 67 deletions(-) diff --git a/contributing/code/tests.rst b/contributing/code/tests.rst index 6e14a9d412f..913c5419b71 100644 --- a/contributing/code/tests.rst +++ b/contributing/code/tests.rst @@ -3,54 +3,32 @@ Running Symfony Tests ===================== -Before submitting a :doc:`patch ` for inclusion, you need to run the -Symfony test suite to check that you have not broken anything. +The Symfony project uses a third-party service which automatically runs tests +for any submitted :doc:`patch `. If the new code breaks any test, +the pull request will show an error message with a link to the full error details. -PHPUnit -------- +In any case, it's a good practice to run tests locally before submitting a +:doc:`patch ` for inclusion, to check that you have not broken anything. -To run the Symfony test suite, `install PHPUnit`_ 4.2 (or later) first. +.. _phpunit: +.. _dependencies_optional: -Dependencies (optional) ------------------------ +Before Running the Tests +------------------------ -To run the entire test suite, including tests that depend on external -dependencies, Symfony needs to be able to autoload them. By default, they are -autoloaded from ``vendor/`` under the main root directory (see -``autoload.php.dist``). - -The test suite needs the following third-party libraries: - -* Doctrine -* Swift Mailer -* Twig -* Monolog - -To install them all, use `Composer`_: - -Step 1: :doc:`Install Composer globally ` - -Step 2: Install vendors. +To run the Symfony test suite, `install PHPUnit`_ 4.2 (or later) first. Then, +install the external dependencies used during the tests, such as Doctrine, Twig +and Monolog. To do so, :doc:`install Composer ` and execute +the following: .. code-block:: bash $ composer install -.. note:: - - Note that the script takes some time to finish. +.. _running: -After installation, you can update the vendors to their latest version with -the follow command: - -.. code-block:: bash - - $ composer update - -Running -------- - -First, update the vendors (see above). +Running the Tests +----------------- Then, run the test suite from the Symfony root directory with the following command: @@ -59,40 +37,17 @@ command: $ phpunit -The output should display ``OK``. If not, you need to figure out what's going on -and if the tests are broken because of your modifications. +The output should display ``OK``. If not, read the reported errors to figure out +what's going on and if the tests are broken because of the new code. .. tip:: - If you want to test a single component type its path after the ``phpunit`` - command, e.g.: + The entire Symfony suite can take up to several minutes to complete. If you + want to test a single component, type its path after the ``phpunit`` command, + e.g.: .. code-block:: bash $ phpunit src/Symfony/Component/Finder/ -.. tip:: - - Run the test suite before applying your modifications to check that they - run fine on your configuration. - -Code Coverage -------------- - -If you add a new feature, you also need to check the code coverage by using -the ``coverage-html`` option: - -.. code-block:: bash - - $ phpunit --coverage-html=cov/ - -Check the code coverage by opening the generated ``cov/index.html`` page in a -browser. - -.. tip:: - - The code coverage only works if you have Xdebug enabled and all - dependencies installed. - -.. _install PHPUnit: https://phpunit.de/manual/current/en/installation.html -.. _`Composer`: https://getcomposer.org/ +.. _`install PHPUnit`: https://phpunit.de/manual/current/en/installation.html From 91b93ecb640c8d8b0093ec8029f648998f37c0eb Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 28 Jul 2015 22:02:04 +0200 Subject: [PATCH 0396/2667] fix YAML syntax highlighting --- cookbook/configuration/mongodb_session_storage.rst | 8 ++++---- cookbook/logging/monolog.rst | 2 +- cookbook/session/locale_sticky_session.rst | 2 +- reference/configuration/doctrine.rst | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cookbook/configuration/mongodb_session_storage.rst b/cookbook/configuration/mongodb_session_storage.rst index c2f912e8b9e..5b2811fab8f 100644 --- a/cookbook/configuration/mongodb_session_storage.rst +++ b/cookbook/configuration/mongodb_session_storage.rst @@ -30,12 +30,12 @@ need to change/add some parameters in the main configuration file: mongo_client: class: MongoClient # if using a username and password - arguments: [mongodb://%mongodb_username%:%mongodb_password%@%mongodb_host%:27017] + arguments: ["mongodb://%mongodb_username%:%mongodb_password%@%mongodb_host%:27017"] # if not using a username and password - arguments: [mongodb://%mongodb_host%:27017] + arguments: ["mongodb://%mongodb_host%:27017"] session.handler.mongo: class: Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler - arguments: [@mongo_client, %mongo.session.options%] + arguments: ["@mongo_client", "%mongo.session.options%"] .. code-block:: xml @@ -168,4 +168,4 @@ From the `MongoDB shell`_: db.session.ensureIndex( { "expireAt": 1 }, { expireAfterSeconds: 0 } ) .. _installed and configured a MongoDB server: http://docs.mongodb.org/manual/installation/ -.. _MongoDB shell: http://docs.mongodb.org/v2.2/tutorial/getting-started-with-the-mongo-shell/ \ No newline at end of file +.. _MongoDB shell: http://docs.mongodb.org/v2.2/tutorial/getting-started-with-the-mongo-shell/ diff --git a/cookbook/logging/monolog.rst b/cookbook/logging/monolog.rst index a7c51bd1c38..666d01d09dc 100644 --- a/cookbook/logging/monolog.rst +++ b/cookbook/logging/monolog.rst @@ -243,7 +243,7 @@ option of your handler to ``rotating_file``: handlers: main: type: rotating_file - path: %kernel.logs_dir%/%kernel.environment%.log + path: "%kernel.logs_dir%/%kernel.environment%.log" level: debug # max number of log files to keep # defaults to zero, which means infinite files diff --git a/cookbook/session/locale_sticky_session.rst b/cookbook/session/locale_sticky_session.rst index 239086b8e4e..c9ac75b55b9 100644 --- a/cookbook/session/locale_sticky_session.rst +++ b/cookbook/session/locale_sticky_session.rst @@ -171,7 +171,7 @@ Then register the listener: services: app.user_locale_listener: class: AppBundle\EventListener\UserLocaleListener - arguments: [@session] + arguments: ["@session"] tags: - { name: kernel.event_listener, event: security.interactive_login, method: onInteractiveLogin } diff --git a/reference/configuration/doctrine.rst b/reference/configuration/doctrine.rst index 1a329f7aa57..3ca1929d53d 100644 --- a/reference/configuration/doctrine.rst +++ b/reference/configuration/doctrine.rst @@ -601,7 +601,7 @@ namespace in the ``src/Entity`` directory and gives them an ``App`` alias # ... SomeEntityNamespace: type: annotation - dir: %kernel.root_dir%/../src/Entity + dir: "%kernel.root_dir%/../src/Entity" is_bundle: false prefix: App\Entity alias: App From 40d8d00fc5913e9621ec4ffcf70f9f15157f2d55 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 28 Jul 2015 23:16:08 +0200 Subject: [PATCH 0397/2667] [#5374] Add deprecation notice in 2.7 --- reference/twig_reference.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 65b866e54a3..55e55195f2c 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -104,9 +104,9 @@ asset **type**: ``string`` ``packageName`` **type**: ``string`` | ``null`` **default**: ``null`` -``absolute`` +``absolute`` (deprecated as of 2.7) **type**: ``boolean`` **default**: ``false`` -``version`` +``version`` (deprecated as of 2.7) **type**: ``string`` **default** ``null`` Returns a public path to ``path``, which takes into account the base path From 9e0f1b448dc5d0c0ef0d1d82acc2671803fd6976 Mon Sep 17 00:00:00 2001 From: Sylvain Combes Date: Sat, 23 May 2015 18:11:36 +0200 Subject: [PATCH 0398/2667] Move some articles from wrong sections --- cookbook/controller/error_pages.rst | 2 +- .../pdo_session_storage.rst | 0 cookbook/event_dispatcher/before_after_filters.rst | 2 +- .../event_listener.rst | 0 cookbook/map.rst.inc | 10 +++++----- cookbook/session/locale_sticky_session.rst | 2 +- cookbook/session/sessions_directory.rst | 2 +- redirection_map | 2 ++ reference/dic_tags.rst | 2 +- 9 files changed, 12 insertions(+), 10 deletions(-) rename cookbook/{configuration => doctrine}/pdo_session_storage.rst (100%) rename cookbook/{service_container => event_dispatcher}/event_listener.rst (100%) diff --git a/cookbook/controller/error_pages.rst b/cookbook/controller/error_pages.rst index a76fd767d24..e2a5de8acc3 100644 --- a/cookbook/controller/error_pages.rst +++ b/cookbook/controller/error_pages.rst @@ -233,7 +233,7 @@ before, but also requires a thorough understanding of Symfony internals. Suppose that your code throws specialized exceptions with a particular meaning to your application domain. -:doc:`Writing your own event listener ` +:doc:`Writing your own event listener ` for the ``kernel.exception`` event allows you to have a closer look at the exception and take different actions depending on it. Those actions might include logging the exception, redirecting the user to another page or rendering specialized diff --git a/cookbook/configuration/pdo_session_storage.rst b/cookbook/doctrine/pdo_session_storage.rst similarity index 100% rename from cookbook/configuration/pdo_session_storage.rst rename to cookbook/doctrine/pdo_session_storage.rst diff --git a/cookbook/event_dispatcher/before_after_filters.rst b/cookbook/event_dispatcher/before_after_filters.rst index d1c5acddbf8..2b7d25d6a9d 100644 --- a/cookbook/event_dispatcher/before_after_filters.rst +++ b/cookbook/event_dispatcher/before_after_filters.rst @@ -102,7 +102,7 @@ Creating an Event Listener Next, you'll need to create an event listener, which will hold the logic that you want executed before your controllers. If you're not familiar with -event listeners, you can learn more about them at :doc:`/cookbook/service_container/event_listener`:: +event listeners, you can learn more about them at :doc:`/cookbook/event_dispatcher/event_listener`:: // src/AppBundle/EventListener/TokenListener.php namespace AppBundle\EventListener; diff --git a/cookbook/service_container/event_listener.rst b/cookbook/event_dispatcher/event_listener.rst similarity index 100% rename from cookbook/service_container/event_listener.rst rename to cookbook/event_dispatcher/event_listener.rst diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 74048d3f60d..e7e5b7e1d99 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -34,7 +34,7 @@ * :doc:`/cookbook/configuration/using_parameters_in_dic` * :doc:`/cookbook/configuration/front_controllers_and_kernel` * :doc:`/cookbook/configuration/external_parameters` - * :doc:`/cookbook/configuration/pdo_session_storage` + * :doc:`/cookbook/doctrine/pdo_session_storage` * :doc:`/cookbook/configuration/apache_router` * :doc:`/cookbook/configuration/web_server_configuration` * :doc:`/cookbook/configuration/configuration_organization` @@ -78,7 +78,7 @@ * :doc:`/cookbook/doctrine/mapping_model_classes` * :doc:`/cookbook/doctrine/registration_form` * :doc:`/cookbook/doctrine/console` - * (configuration) :doc:`/cookbook/configuration/pdo_session_storage` + * (configuration) :doc:`/cookbook/doctrine/pdo_session_storage` * :doc:`/cookbook/email/index` @@ -94,7 +94,7 @@ * :doc:`/cookbook/event_dispatcher/before_after_filters` * :doc:`/cookbook/event_dispatcher/class_extension` * :doc:`/cookbook/event_dispatcher/method_behavior` - * (service container) :doc:`/cookbook/service_container/event_listener` + * (service container) :doc:`/cookbook/event_dispatcher/event_listener` * :doc:`/cookbook/form/index` @@ -179,7 +179,7 @@ * :doc:`/cookbook/service_container/index` - * :doc:`/cookbook/service_container/event_listener` + * :doc:`/cookbook/event_dispatcher/event_listener` * :doc:`/cookbook/service_container/scopes` * :doc:`/cookbook/service_container/compiler_passes` @@ -189,7 +189,7 @@ * :doc:`/cookbook/session/locale_sticky_session` * :doc:`/cookbook/session/sessions_directory` * :doc:`/cookbook/session/php_bridge` - * (configuration) :doc:`/cookbook/configuration/pdo_session_storage` + * (configuration) :doc:`/cookbook/doctrine/pdo_session_storage` * (configuration) :doc:`/cookbook/configuration/mongodb_session_storage` * :doc:`/cookbook/session/avoid_session_start` diff --git a/cookbook/session/locale_sticky_session.rst b/cookbook/session/locale_sticky_session.rst index c9ac75b55b9..978ab473764 100644 --- a/cookbook/session/locale_sticky_session.rst +++ b/cookbook/session/locale_sticky_session.rst @@ -14,7 +14,7 @@ Creating a LocaleListener ------------------------- To simulate that the locale is stored in a session, you need to create and -register a :doc:`new event listener `. +register a :doc:`new event listener `. The listener will look something like this. Typically, ``_locale`` is used as a routing parameter to signify the locale, though it doesn't really matter how you determine the desired locale from the request:: diff --git a/cookbook/session/sessions_directory.rst b/cookbook/session/sessions_directory.rst index 5ecc133ae9f..13414826dc0 100644 --- a/cookbook/session/sessions_directory.rst +++ b/cookbook/session/sessions_directory.rst @@ -94,7 +94,7 @@ that your current sessions aren't lost when you clear Symfony's cache. method of session management available within Symfony. See :doc:`/components/http_foundation/session_configuration` for a discussion of session save handlers. There are also entries in the cookbook - about storing sessions in a :doc:`relational database ` + about storing sessions in a :doc:`relational database ` or a :doc:`NoSQL database `. To change the directory in which Symfony saves session data, you only need diff --git a/redirection_map b/redirection_map index 1ebc69ced38..d98d5e697e3 100644 --- a/redirection_map +++ b/redirection_map @@ -26,3 +26,5 @@ /components/templating /components/templating/introduction /cookbook/upgrading /cookbook/upgrade/index /cookbook/security/voters_data_permission /cookbook/security/voters +/cookbook/configuration/pdo_session_storage /cookbook/doctrine/pdo_session_storage +/cookbook/service_container/event_listener /cookbook/event_dispatcher/event_listener diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 67a2a8aaa1b..fe56ed9ce3b 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -543,7 +543,7 @@ kernel.event_listener This tag allows you to hook your own classes into Symfony's process at different points. -For a full example of this listener, read the :doc:`/cookbook/service_container/event_listener` +For a full example of this listener, read the :doc:`/cookbook/event_dispatcher/event_listener` cookbook entry. For another practical example of a kernel listener, see the cookbook From fb7be616292a1cdc764b3bcfa27c0584cbb2ffd5 Mon Sep 17 00:00:00 2001 From: Sylvain Combes Date: Sat, 23 May 2015 18:57:14 +0200 Subject: [PATCH 0399/2667] Move some articles from wrong sections, updated indexs --- cookbook/configuration/index.rst | 1 - cookbook/doctrine/index.rst | 1 + cookbook/event_dispatcher/index.rst | 1 + cookbook/service_container/index.rst | 1 - 4 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/configuration/index.rst b/cookbook/configuration/index.rst index 8bac5cf43be..c53f6e0a6bd 100644 --- a/cookbook/configuration/index.rst +++ b/cookbook/configuration/index.rst @@ -9,7 +9,6 @@ Configuration using_parameters_in_dic front_controllers_and_kernel external_parameters - pdo_session_storage apache_router web_server_configuration configuration_organization diff --git a/cookbook/doctrine/index.rst b/cookbook/doctrine/index.rst index a62c736db11..1c9565871c5 100644 --- a/cookbook/doctrine/index.rst +++ b/cookbook/doctrine/index.rst @@ -14,4 +14,5 @@ Doctrine resolve_target_entity mapping_model_classes registration_form + pdo_session_storage console diff --git a/cookbook/event_dispatcher/index.rst b/cookbook/event_dispatcher/index.rst index 8dfe9a541f4..27a9adfcc3d 100644 --- a/cookbook/event_dispatcher/index.rst +++ b/cookbook/event_dispatcher/index.rst @@ -4,6 +4,7 @@ Event Dispatcher .. toctree:: :maxdepth: 2 + event_listener before_after_filters class_extension method_behavior diff --git a/cookbook/service_container/index.rst b/cookbook/service_container/index.rst index be8ad17868b..f66a455b788 100644 --- a/cookbook/service_container/index.rst +++ b/cookbook/service_container/index.rst @@ -4,6 +4,5 @@ Service Container .. toctree:: :maxdepth: 2 - event_listener scopes compiler_passes From e755522cae64ec15e811e4028439658acf87a6f7 Mon Sep 17 00:00:00 2001 From: Sylvain Combes Date: Tue, 2 Jun 2015 21:51:02 +0200 Subject: [PATCH 0400/2667] Fixing remaining reference of old emplacement of pdo_session_storage page due to the rebase --- reference/configuration/framework.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 75af745849a..cace8c57949 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -713,7 +713,7 @@ installation. .. seealso:: You can see an example of the usage of this in - :doc:`/cookbook/configuration/pdo_session_storage`. + :doc:`/cookbook/doctrine/pdo_session_storage`. name .... From 0b3da6fc6612a5b8b23983c7ab71690b2b8cc761 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 28 Jul 2015 23:53:01 +0200 Subject: [PATCH 0401/2667] Moved mongodb article and cleanup --- cookbook/configuration/index.rst | 1 - cookbook/doctrine/index.rst | 1 + .../mongodb_session_storage.rst | 0 cookbook/map.rst.inc | 19 ++++++++++--------- cookbook/session/sessions_directory.rst | 2 +- redirection_map | 1 + 6 files changed, 13 insertions(+), 11 deletions(-) rename cookbook/{configuration => doctrine}/mongodb_session_storage.rst (100%) diff --git a/cookbook/configuration/index.rst b/cookbook/configuration/index.rst index c53f6e0a6bd..11838c6fa38 100644 --- a/cookbook/configuration/index.rst +++ b/cookbook/configuration/index.rst @@ -12,4 +12,3 @@ Configuration apache_router web_server_configuration configuration_organization - mongodb_session_storage \ No newline at end of file diff --git a/cookbook/doctrine/index.rst b/cookbook/doctrine/index.rst index 1c9565871c5..d14ecb20536 100644 --- a/cookbook/doctrine/index.rst +++ b/cookbook/doctrine/index.rst @@ -15,4 +15,5 @@ Doctrine mapping_model_classes registration_form pdo_session_storage + mongodb_session_storage console diff --git a/cookbook/configuration/mongodb_session_storage.rst b/cookbook/doctrine/mongodb_session_storage.rst similarity index 100% rename from cookbook/configuration/mongodb_session_storage.rst rename to cookbook/doctrine/mongodb_session_storage.rst diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index e7e5b7e1d99..5ba5d97ff1c 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -34,11 +34,11 @@ * :doc:`/cookbook/configuration/using_parameters_in_dic` * :doc:`/cookbook/configuration/front_controllers_and_kernel` * :doc:`/cookbook/configuration/external_parameters` - * :doc:`/cookbook/doctrine/pdo_session_storage` * :doc:`/cookbook/configuration/apache_router` * :doc:`/cookbook/configuration/web_server_configuration` * :doc:`/cookbook/configuration/configuration_organization` - * :doc:`/cookbook/configuration/mongodb_session_storage` + * (Doctrine) :doc:`/cookbook/doctrine/pdo_session_storage` + * (Doctrine) :doc:`/cookbook/doctrine/mongodb_session_storage` * :doc:`/cookbook/console/index` @@ -77,8 +77,9 @@ * :doc:`/cookbook/doctrine/resolve_target_entity` * :doc:`/cookbook/doctrine/mapping_model_classes` * :doc:`/cookbook/doctrine/registration_form` + * :doc:`/cookbook/doctrine/pdo_session_storage` + * :doc:`/cookbook/doctrine/mongodb_session_storage` * :doc:`/cookbook/doctrine/console` - * (configuration) :doc:`/cookbook/doctrine/pdo_session_storage` * :doc:`/cookbook/email/index` @@ -94,7 +95,7 @@ * :doc:`/cookbook/event_dispatcher/before_after_filters` * :doc:`/cookbook/event_dispatcher/class_extension` * :doc:`/cookbook/event_dispatcher/method_behavior` - * (service container) :doc:`/cookbook/event_dispatcher/event_listener` + * :doc:`/cookbook/event_dispatcher/event_listener` * :doc:`/cookbook/form/index` @@ -108,8 +109,8 @@ * :doc:`/cookbook/form/unit_testing` * :doc:`/cookbook/form/use_empty_data` * :doc:`/cookbook/form/direct_submit` - * (validation) :doc:`/cookbook/validation/custom_constraint` - * (doctrine) :doc:`/cookbook/doctrine/file_uploads` + * (Validation) :doc:`/cookbook/validation/custom_constraint` + * (Doctrine) :doc:`/cookbook/doctrine/file_uploads` * :doc:`/cookbook/frontend/index` @@ -179,9 +180,9 @@ * :doc:`/cookbook/service_container/index` - * :doc:`/cookbook/event_dispatcher/event_listener` * :doc:`/cookbook/service_container/scopes` * :doc:`/cookbook/service_container/compiler_passes` + * (Event Dispatcher) :doc:`/cookbook/event_dispatcher/event_listener` * :doc:`/cookbook/session/index` @@ -189,9 +190,9 @@ * :doc:`/cookbook/session/locale_sticky_session` * :doc:`/cookbook/session/sessions_directory` * :doc:`/cookbook/session/php_bridge` - * (configuration) :doc:`/cookbook/doctrine/pdo_session_storage` - * (configuration) :doc:`/cookbook/configuration/mongodb_session_storage` * :doc:`/cookbook/session/avoid_session_start` + * (Doctrine) :doc:`/cookbook/doctrine/pdo_session_storage` + * (Doctrine) :doc:`/cookbook/doctrine/mongodb_session_storage` * **PSR-7** diff --git a/cookbook/session/sessions_directory.rst b/cookbook/session/sessions_directory.rst index 13414826dc0..3b62b62cb95 100644 --- a/cookbook/session/sessions_directory.rst +++ b/cookbook/session/sessions_directory.rst @@ -95,7 +95,7 @@ that your current sessions aren't lost when you clear Symfony's cache. :doc:`/components/http_foundation/session_configuration` for a discussion of session save handlers. There are also entries in the cookbook about storing sessions in a :doc:`relational database ` - or a :doc:`NoSQL database `. + or a :doc:`NoSQL database `. To change the directory in which Symfony saves session data, you only need change the framework configuration. In this example, you will change the diff --git a/redirection_map b/redirection_map index d98d5e697e3..2abbaf55500 100644 --- a/redirection_map +++ b/redirection_map @@ -27,4 +27,5 @@ /cookbook/upgrading /cookbook/upgrade/index /cookbook/security/voters_data_permission /cookbook/security/voters /cookbook/configuration/pdo_session_storage /cookbook/doctrine/pdo_session_storage +/cookbook/configuration/mongodb_session_storage /cookbook/doctrine/mongodb_session_storage /cookbook/service_container/event_listener /cookbook/event_dispatcher/event_listener From 11383f8ad5be5abed7fe006110a57a8b518809ca Mon Sep 17 00:00:00 2001 From: Henry Snoek Date: Thu, 28 May 2015 12:41:39 +0200 Subject: [PATCH 0402/2667] 4668 document isCsrfTokenValid --- book/controller.rst | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/book/controller.rst b/book/controller.rst index 7ec7bd4ae3a..18555aeb521 100644 --- a/book/controller.rst +++ b/book/controller.rst @@ -440,7 +440,7 @@ If you want to redirect the user to another page, use the ``redirectToRoute()`` } .. versionadded:: 2.6 - The ``redirectToRoute()`` method was added in Symfony 2.6. Previously (and still now), you + The ``redirectToRoute()`` method was introduced in Symfony 2.6. Previously (and still now), you could use ``redirect()`` and ``generateUrl()`` together for this (see the example above). Or, if you want to redirect externally, just use ``redirect()`` and pass it the URL:: @@ -803,6 +803,28 @@ Just like when creating a controller for a route, the order of the arguments of order of the arguments, Symfony will still pass the correct value to each variable. +Validating a CSRF Token +----------------------- + +Sometimes you want to use CSRF protection in an action where you don't want to use the +Symfony Form component. + +If, for example, you're doing a DELETE action, you can use the +:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::isCsrfTokenValid` +method to check the CSRF token:: + + if ($this->isCsrfTokenValid('token_id', $submittedToken)) { + // ... do something, like deleting an object + } + +.. versionadded:: 2.6 + The ``isCsrfTokenValid()`` shortcut method was introduced in Symfony 2.6. + It is equivalent to executing the following code:: + + use Symfony\Component\Security\Csrf\CsrfToken; + + $this->get('security.csrf.token_manager')->isTokenValid(new CsrfToken('token_id', 'TOKEN')); + Final Thoughts -------------- From f67c353b576d5b1e65d72b36db939b85f1833913 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Wed, 29 Jul 2015 13:53:52 +0200 Subject: [PATCH 0403/2667] [#5572] Fix syntax --- book/controller.rst | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/book/controller.rst b/book/controller.rst index 18555aeb521..8b1f2dc7a47 100644 --- a/book/controller.rst +++ b/book/controller.rst @@ -806,11 +806,9 @@ variable. Validating a CSRF Token ----------------------- -Sometimes you want to use CSRF protection in an action where you don't want to use the -Symfony Form component. - -If, for example, you're doing a DELETE action, you can use the -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::isCsrfTokenValid` +Sometimes, you want to use CSRF protection in an action where you don't want to +use the Symfony Form component. If, for example, you're doing a DELETE action, +you can use the :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::isCsrfTokenValid` method to check the CSRF token:: if ($this->isCsrfTokenValid('token_id', $submittedToken)) { @@ -821,9 +819,10 @@ method to check the CSRF token:: The ``isCsrfTokenValid()`` shortcut method was introduced in Symfony 2.6. It is equivalent to executing the following code:: - use Symfony\Component\Security\Csrf\CsrfToken; + use Symfony\Component\Security\Csrf\CsrfToken; - $this->get('security.csrf.token_manager')->isTokenValid(new CsrfToken('token_id', 'TOKEN')); + $this->get('security.csrf.token_manager') + ->isTokenValid(new CsrfToken('token_id', 'TOKEN')); Final Thoughts -------------- From 9ad9dafbd373c8d1214345242298e86c79244462 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 25 May 2015 19:11:29 -0400 Subject: [PATCH 0404/2667] Additional little check to show how we're assumign the User object is the User entity --- cookbook/security/voters.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index e0d5c3868c2..8b1ab715e11 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -77,6 +77,7 @@ edit a particular object. Here's an example implementation:: // src/AppBundle/Security/Authorization/Voter/PostVoter.php namespace AppBundle\Security\Authorization\Voter; + use AppBundle\Entity\User; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -133,6 +134,13 @@ edit a particular object. Here's an example implementation:: return VoterInterface::ACCESS_DENIED; } + // double-check that the User object is the expected entity. + // It always will be, unless there is some misconfiguration of the + // security system. + if (!$user instanceof User) { + throw new \LogicException('The user is somehow not our User class!'); + } + switch($attribute) { case self::VIEW: // the data object could have for example a method isPrivate() From 1e1a129c8dec5cc6fac810ea677f1f35f3fae542 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 1 Aug 2015 16:11:50 +0200 Subject: [PATCH 0405/2667] [Cookbook][Session] fix default expiry field name --- cookbook/doctrine/mongodb_session_storage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/doctrine/mongodb_session_storage.rst b/cookbook/doctrine/mongodb_session_storage.rst index 5b2811fab8f..12f304a2754 100644 --- a/cookbook/doctrine/mongodb_session_storage.rst +++ b/cookbook/doctrine/mongodb_session_storage.rst @@ -165,7 +165,7 @@ From the `MongoDB shell`_: .. code-block:: sql use session_db - db.session.ensureIndex( { "expireAt": 1 }, { expireAfterSeconds: 0 } ) + db.session.ensureIndex( { "expires_at": 1 }, { expireAfterSeconds: 0 } ) .. _installed and configured a MongoDB server: http://docs.mongodb.org/manual/installation/ .. _MongoDB shell: http://docs.mongodb.org/v2.2/tutorial/getting-started-with-the-mongo-shell/ From 4b23fdc6d2953c0783d4f9ea9f1fbb754464b691 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 4 Aug 2015 23:23:31 +0200 Subject: [PATCH 0406/2667] don't override existing variables When extracting the request attributes, existing variables must not be overridden so that the `$request` variable is passed to the template as is. --- create_framework/templating.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/create_framework/templating.rst b/create_framework/templating.rst index 987a9ea26d1..43e77f92623 100644 --- a/create_framework/templating.rst +++ b/create_framework/templating.rst @@ -41,7 +41,7 @@ rendered:: function render_template($request) { - extract($request->attributes->all()); + extract($request->attributes->all(), EXTR_SKIP); ob_start(); include sprintf(__DIR__.'/../src/pages/%s.php', $_route); @@ -110,7 +110,7 @@ Here is the updated and improved version of our framework:: function render_template($request) { - extract($request->attributes->all()); + extract($request->attributes->all(), EXTR_SKIP); ob_start(); include sprintf(__DIR__.'/../src/pages/%s.php', $_route); From 8f26ed50d04a961064b4ef195107b4c02f939cf7 Mon Sep 17 00:00:00 2001 From: Alex Wybraniec Date: Sun, 2 Aug 2015 21:24:29 +0100 Subject: [PATCH 0407/2667] Update templating.rst Version 2.7 says that the parameter order has change from twig:lint to lint:twig me@host:/vagrant/_project$ php app/console twig:lint app/Resources/views The use of "twig:lint" command is deprecated since version 2.7 and will be removed in 3.0. Use the "lint:twig" instead. 8/8 valid files me@host:/vagrant/_project$ php app/console lint:twig app/Resources/views 8/8 valid files Also fixed example in paragraph above. --- book/templating.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/book/templating.rst b/book/templating.rst index e1159069d71..19ecf3fa61a 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -1224,7 +1224,7 @@ automatically: .. versionadded:: 2.6 The global ``app.security`` variable (or the ``$app->getSecurity()`` - method in PHP templates) is deprecated as of Symfony 2.6. Use ``app.user`` + method in PHP templates) is deprecated as of Symfony 2.6. Use ``app.user`` (``$app->getUser()``) and ``is_granted()`` (``$view['security']->isGranted()``) instead. @@ -1595,16 +1595,16 @@ is ``true``. By default this means that the variables will be dumped in the Syntax Checking --------------- -You can check for syntax errors in Twig templates using the ``twig:lint`` +You can check for syntax errors in Twig templates using the ``lint:twig`` console command: .. code-block:: bash # You can check by filename: - $ php app/console twig:lint app/Resources/views/article/recent_list.html.twig + $ php app/console lint:twig app/Resources/views/article/recent_list.html.twig # or by directory: - $ php app/console twig:lint app/Resources/views + $ php app/console lint:twig app/Resources/views .. _template-formats: From 572c29dcda1ada32a235a530e288db7fd35ae7d3 Mon Sep 17 00:00:00 2001 From: Dmytro Bazavluk Date: Wed, 5 Aug 2015 09:16:57 -0500 Subject: [PATCH 0408/2667] Update lazy_services.rst --- components/dependency_injection/lazy_services.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/dependency_injection/lazy_services.rst b/components/dependency_injection/lazy_services.rst index 3ed6fb33678..fa6d81968bd 100644 --- a/components/dependency_injection/lazy_services.rst +++ b/components/dependency_injection/lazy_services.rst @@ -40,7 +40,7 @@ the `ProxyManager bridge`_: .. code-block:: bash - $ php composer.phar require ocramius/proxy-manager:~1.0 + $ composer require ocramius/proxy-manager:~1.0 Afterwards compile your container and check to make sure that you get a proxy for your lazy services. From f44cccf719749cacf9702b26facc4d8ef7847397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yonel=20Ceruto=20Gonz=C3=A1lez?= Date: Fri, 26 Jun 2015 15:36:21 -0400 Subject: [PATCH 0409/2667] Ensure that the entity is updated. | Q | A | ------------- | --- | Doc fix? | yes | New docs? | no | Applies to | All | Fixed tickets | #5448 --- cookbook/doctrine/file_uploads.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/cookbook/doctrine/file_uploads.rst b/cookbook/doctrine/file_uploads.rst index 716d42205f7..96269168bb0 100644 --- a/cookbook/doctrine/file_uploads.rst +++ b/cookbook/doctrine/file_uploads.rst @@ -494,6 +494,7 @@ property, instead of the actual filename:: if (is_file($this->getAbsolutePath())) { // store the old name to delete after the update $this->temp = $this->getAbsolutePath(); + $this->path = null; } else { $this->path = 'initial'; } From 740e58db87b459ba7ed790a149cd99af2e197b93 Mon Sep 17 00:00:00 2001 From: Fabien Schurter Date: Sun, 9 Aug 2015 19:25:45 +0200 Subject: [PATCH 0410/2667] Add a missing backtick --- create_framework/templating.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_framework/templating.rst b/create_framework/templating.rst index 987a9ea26d1..5d233ecfe37 100644 --- a/create_framework/templating.rst +++ b/create_framework/templating.rst @@ -173,7 +173,7 @@ framework does not need to be modified in any way, just create a new The ``is_leap_year()`` function returns ``true`` when the given year is a leap year, ``false`` otherwise. If the year is ``null``, the current year is tested. The controller is simple: it gets the year from the request -attributes, pass it to the `is_leap_year()`` function, and according to the +attributes, pass it to the ``is_leap_year()`` function, and according to the return value it creates a new Response object. As always, you can decide to stop here and use the framework as is; it's From 422f5c77f23eb98453291f5961252d75e5da5acf Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 9 Aug 2015 20:26:33 +0200 Subject: [PATCH 0411/2667] Added July changelog --- changelog.rst | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/changelog.rst b/changelog.rst index 75591224909..95fe8aa23c3 100644 --- a/changelog.rst +++ b/changelog.rst @@ -13,6 +13,76 @@ documentation. Do you also want to participate in the Symfony Documentation? Take a look at the ":doc:`/contributing/documentation/overview`" article. +July, 2015 +---------- + +New Documentation +~~~~~~~~~~~~~~~~~ + +* `#5533 `_ Replace Capifony with Capistrano/symfony (mojz 10000 is) +* `#5516 `_ Added a note about session data size in PdoSessionHandler (javiereguiluz) +* `#5491 `_ added composer info (OskarStark) +* `#5478 `_ Add cookbook article for using MongoDB to store session data (stevenmusumeche) +* `#5472 `_ Added a tip about hashing the result of nextBytes() (javiereguiluz) +* `#5453 `_ Cleanup security voters cookbook recipes (WouterJ) +* `#5201 `_ [Book][Routing] Add example about how to match multiple methods (xelaris) +* `#5430 `_ Pr/5085 (sjagr, javiereguiluz) +* `#5456 `_ Completely re-reading the data transformers chapter (weaverryan) +* `#5426 `_ Documented the checkDNS option of the Url validator (saro0h, javiereguiluz) +* `#5424 `_ Integrate the "Create your own framework" tutorial (fabpot, lyrixx, jdreesen, catchamonkey, gnugat, andreia, Arnaud Kleinpeter, willdurand, amitayh, nanocom, hrbonz, Pedro Gimenez, ubick, dirkaholic, bamarni, revollat, javiereguiluz) + +Fixed Documentation +~~~~~~~~~~~~~~~~~~~ + +* `#5567 `_ Change Sql Field name because it's reserved (rmed19) +* `#5528 `_ [reate_framework] Fix mock $matcher (kenjis) +* `#5501 `_ Fix typo in url for PHPUnit test coverage report (TrueGit) +* `#5461 `_ Rework quick tour big picture (smatejic, DQNEO, xabbuh) +* `#5488 `_ fix #5487 (emillosanti) +* `#5496 `_ Security voters fixes (german.bortoli) +* `#5424 `_ Integrate the "Create your own framework" tutorial (fabpot, lyrixx, jdreesen, catchamonkey, gnugat, andreia, Arnaud Kleinpeter, willdurand, amitayh, nanocom, hrbonz, Pedro Gimenez, ubick, dirkaholic, bamarni, revollat, javiereguiluz) + +Minor Documentation Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* `#5575 `_ Move some articles from wrong sections (sylvaincombes, WouterJ) +* `#5580 `_ Additional User check in voter class (weaverryan) +* `#5573 `_ fix YAML syntax highlighting (xabbuh) +* `#5564 `_ Improve and simplify the contributing instructions about tests (javiereguiluz) +* `#5550 `_ [docbot] Reviewed some component chapters (WouterJ) +* `#5556 `_ Fix typo Esi in part create framework (nicolasdewez) +* `#5568 `_ [Create Framework] Fix extract calls (replaces #5522) (kenjis) +* `#5548 `_ use the include() Twig function instead of the tag (xabbuh) +* `#5557 `_ [2.3] [Contributing] Added note about empty returns (phansys) +* `#5492 `_ updated tree for front controller (OskarStark) +* `#5536 `_ Removed reference to remove HTTPS off from nginx configuration (wjzijderveld) +* `#5545 `_ Misc. improvements in the Console component introduction (javiereguiluz) +* `#5512 `_ [Cookbook] Backport PSR-7 bridge docs to 2.3 (dunglas, weaverryan) +* `#5494 `_ updated tree (OskarStark) +* `#5490 `_ changed headline (OskarStark) +* `#5479 `_ Update http-foundation.rst (jezemery) +* `#5552 `_ rename $input to $greetInput (Xosofox) +* `#5537 `_ Update design patter of Event Dispatcher (almacbe) +* `#5546 `_ A bunch of doc fixes again (WouterJ) +* `#5486 `_ review all Security code blocks (xabbuh) +* `#5529 `_ [Cookbook][upload_file] Fix :methods: to remove doubled braces (bicpi) +* `#5455 `_ Improve travis build speed (WouterJ) +* `#5442 `_ Improved the explanation about the verbosity levels of the console (javiereguiluz) +* `#5519 `_ Prepare Platform.sh configuration files. (GuGuss) +* `#5518 `_ Minor grammar fix. (maxolasersquad) +* `#5427 `_ Cookbook grammar and style fixes (frne, javiereguiluz) +* `#5505 `_ [Cookbook][Form] some tweaks to the data transformers chapter (xabbuh) +* `#5352 `_ Update http_fundamentals.rst (wouthoekstra) +* `#5471 `_ Updated the Symfony Versions Roadmap image (javiereguiluz) +* `#5511 `_ [HttpKernel] Fix use statement (dunglas) +* `#5506 `_ Fixes small typo in data transformers cookbook (catchamonkey) +* `#5425 `_ Added a caution note about invoking other commands (kix, javiereguiluz) +* `#5367 `_ Split Security into Authentication & Authorization (iltar) +* `#5485 `_ Fix invalid phpunit URLs (norkunas) +* `#5473 `_ --dev is default and causes a warning (DQNEO) +* `#5474 `_ typo in components/translation/instruction.rst (beesofts) + + June, 2015 ---------- From 1a7f1f5604538b3d2cc34b69431737125871b797 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 9 Aug 2015 20:40:24 +0200 Subject: [PATCH 0412/2667] Added July changelog --- changelog.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/changelog.rst b/changelog.rst index 726d5706572..701bc7c37d6 100644 --- a/changelog.rst +++ b/changelog.rst @@ -20,15 +20,19 @@ New Documentation ~~~~~~~~~~~~~~~~~ * `#5533 `_ Replace Capifony with Capistrano/symfony (mojzis) +* `#5543 `_ Add deprecation notice to "choice_list" option of ChoiceType (XitasoChris) * `#5516 `_ Added a note about session data size in PdoSessionHandler (javiereguiluz) +* `#5499 `_ The "property" option of DoctrineType was deprecated. (XWB) * `#5491 `_ added composer info (OskarStark) * `#5478 `_ Add cookbook article for using MongoDB to store session data (stevenmusumeche) * `#5472 `_ Added a tip about hashing the result of nextBytes() (javiereguiluz) * `#5453 `_ Cleanup security voters cookbook recipes (WouterJ) +* `#5444 `_ Documented the "auto_alias" feature (javiereguiluz) * `#5201 `_ [Book][Routing] Add example about how to match multiple methods (xelaris) * `#5430 `_ Pr/5085 (sjagr, javiereguiluz) * `#5456 `_ Completely re-reading the data transformers chapter (weaverryan) * `#5426 `_ Documented the checkDNS option of the Url validator (saro0h, javiereguiluz) +* `#5333 `_ [FrameworkBundle] Update serializer configuration reference (dunglas) * `#5424 `_ Integrate the "Create your own framework" tutorial (fabpot, lyrixx, jdreesen, catchamonkey, gnugat, andreia, Arnaud Kleinpeter, willdurand, amitayh, nanocom, hrbonz, Pedro Gimenez, ubick, dirkaholic, bamarni, revollat, javiereguiluz) Fixed Documentation @@ -37,6 +41,7 @@ Fixed Documentation * `#5567 `_ Change Sql Field name because it's reserved (rmed19) * `#5528 `_ [reate_framework] Fix mock $matcher (kenjis) * `#5501 `_ Fix typo in url for PHPUnit test coverage report (TrueGit) +* `#5501 `_ Fix typo in url for PHPUnit test coverage report (TrueGit) * `#5461 `_ Rework quick tour big picture (smatejic, DQNEO, xabbuh) * `#5488 `_ fix #5487 (emillosanti) * `#5496 `_ Security voters fixes (german.bortoli) @@ -53,6 +58,9 @@ Minor Documentation Changes * `#5556 `_ Fix typo Esi in part create framework (nicolasdewez) * `#5568 `_ [Create Framework] Fix extract calls (replaces #5522) (kenjis) * `#5548 `_ use the include() Twig function instead of the tag (xabbuh) +* `#5542 `_ [Cookbook][Email] add missing versionadded directive (xabbuh) +* `#5476 `_ [Cookbook][Security] some additional tweaks for the voter cookbook (xabbuh) +* `#5413 `_ Fix doc about deprecations policy (nicolas-grekas) * `#5557 `_ [2.3] [Contributing] Added note about empty returns (phansys) * `#5492 `_ updated tree for front controller (OskarStark) * `#5536 `_ Removed reference to remove HTTPS off from nginx configuration (wjzijderveld) @@ -62,19 +70,27 @@ Minor Documentation Changes * `#5490 `_ changed headline (OskarStark) * `#5479 `_ Update http-foundation.rst (jezemery) * `#5552 `_ rename $input to $greetInput (Xosofox) +* `#5544 `_ [components][expression_language] Fix the wrong constructor for SerializedParsedExpression (zerustech) * `#5537 `_ Update design patter of Event Dispatcher (almacbe) * `#5546 `_ A bunch of doc fixes again (WouterJ) * `#5486 `_ review all Security code blocks (xabbuh) +* `#5538 `_ Update email.rst (TisLars) * `#5529 `_ [Cookbook][upload_file] Fix :methods: to remove doubled braces (bicpi) * `#5455 `_ Improve travis build speed (WouterJ) * `#5442 `_ Improved the explanation about the verbosity levels of the console (javiereguiluz) +* `#5523 `_ Custom voter example, fix missing curly brace (snroki) +* `#5524 `_ TYPO: missing closing parantheses of the array (listerical85) * `#5519 `_ Prepare Platform.sh configuration files. (GuGuss) +* `#5443 `_ Added a note about the implementation of the verbosity semantic methods (javiereguiluz) * `#5518 `_ Minor grammar fix. (maxolasersquad) +* `#5520 `_ Fix RST (kenjis) +* `#5429 `_ Promote Symfony's builtin serializer instead of JMS (javiereguiluz) * `#5427 `_ Cookbook grammar and style fixes (frne, javiereguiluz) * `#5505 `_ [Cookbook][Form] some tweaks to the data transformers chapter (xabbuh) * `#5352 `_ Update http_fundamentals.rst (wouthoekstra) * `#5471 `_ Updated the Symfony Versions Roadmap image (javiereguiluz) * `#5511 `_ [HttpKernel] Fix use statement (dunglas) +* `#5510 `_ [PSR-7] Fix Diactoros link (dunglas) * `#5506 `_ Fixes small typo in data transformers cookbook (catchamonkey) * `#5425 `_ Added a caution note about invoking other commands (kix, javiereguiluz) * `#5367 `_ Split Security into Authentication & Authorization (iltar) From 9979a723a3419c6971de51c74270d3047ec34087 Mon Sep 17 00:00:00 2001 From: Fabien Schurter Date: Mon, 10 Aug 2015 12:25:37 +0200 Subject: [PATCH 0413/2667] Remove unneeded backtick --- create_framework/http-kernel-controller-resolver.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_framework/http-kernel-controller-resolver.rst b/create_framework/http-kernel-controller-resolver.rst index b0393b4cdd3..261ab548cf9 100644 --- a/create_framework/http-kernel-controller-resolver.rst +++ b/create_framework/http-kernel-controller-resolver.rst @@ -91,7 +91,7 @@ introspects the controller signature to determine which arguments to pass to it by using the native PHP `reflection`_. The ``indexAction()`` method needs the Request object as an argument. -```getArguments()`` knows when to inject it properly if it is type-hinted +``getArguments()`` knows when to inject it properly if it is type-hinted correctly:: public function indexAction(Request $request) From d38e527d91900eb9b5f3c83d8b789300deba2ae5 Mon Sep 17 00:00:00 2001 From: Mohammed Rhamnia Date: Mon, 10 Aug 2015 19:52:56 +0100 Subject: [PATCH 0414/2667] Add Body tag to see the web debug toolbar --- book/security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/security.rst b/book/security.rst index 1d1259d5e90..7016c00a5ed 100644 --- a/book/security.rst +++ b/book/security.rst @@ -193,7 +193,7 @@ example, if you use annotations, create something like this:: */ public function adminAction() { - return new Response('Admin page!'); + return new Response('Admin page!'); } } From ceacc7c4a257da79944d710c35945666ce14c6bb Mon Sep 17 00:00:00 2001 From: Ben Thomas Date: Tue, 11 Aug 2015 23:11:38 -0400 Subject: [PATCH 0415/2667] Explain sample configuration parameter usage The "Processing the ``$configs`` Array" section explains how to define parameters within the Configuration class and process them in the Extension. In my opinion, it doesn't take the example to a logical conclusion. People reading this file want to learn how to create configuration within a Bundle. It would be helpful to explain how the configuration can be used. Since the example uses parameters, I propose showing how the parameters can be loaded into the container. With these code changes, the `twitter.client_id` and `twitter.client_secret` are now available through use throughout the application. --- cookbook/bundles/configuration.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cookbook/bundles/configuration.rst b/cookbook/bundles/configuration.rst index f04f1018ccf..ad24bd33ad2 100644 --- a/cookbook/bundles/configuration.rst +++ b/cookbook/bundles/configuration.rst @@ -223,7 +223,12 @@ thrown):: $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); - // ... + + // Example configuration parameter usage: Set configuration variables as + // parameters in the container. + $container->setParameter('twitter.client_id', $config['twitter']['client_id']); + $container->setParameter('twitter.client_secret', $config['twitter']['client_secret']); + } The ``processConfiguration()`` method uses the configuration tree you've defined From edcdb3ae9785543232776d6aad778ff426337b7a Mon Sep 17 00:00:00 2001 From: Kevin Wojniak Date: Wed, 12 Aug 2015 10:56:43 -0700 Subject: [PATCH 0416/2667] the_architecture: Fix syntax error --- quick_tour/the_architecture.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quick_tour/the_architecture.rst b/quick_tour/the_architecture.rst index 4259cadba7b..8fdd851376a 100644 --- a/quick_tour/the_architecture.rst +++ b/quick_tour/the_architecture.rst @@ -114,7 +114,7 @@ a single Bundle class that describes it:: new Symfony\Bundle\DoctrineBundle\DoctrineBundle(), new Symfony\Bundle\AsseticBundle\AsseticBundle(), new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), - new AppBundle\AppBundle(); + new AppBundle\AppBundle(), ); if (in_array($this->getEnvironment(), array('dev', 'test'))) { From 3a3d55310e9eaef0dff9f1c4f0a16180788a31d0 Mon Sep 17 00:00:00 2001 From: Maxime Douailin Date: Wed, 12 Aug 2015 10:05:06 +0200 Subject: [PATCH 0417/2667] typo fix in pre authenticated --- cookbook/security/pre_authenticated.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/security/pre_authenticated.rst b/cookbook/security/pre_authenticated.rst index 183fe9e91ca..e3501c54884 100644 --- a/cookbook/security/pre_authenticated.rst +++ b/cookbook/security/pre_authenticated.rst @@ -149,5 +149,5 @@ key in the ``remote_user`` firewall configuration. .. note:: Just like for X509 authentication, you will need to configure a "user provider". - See :ref:`the note previous note ` + See :ref:`the previous note ` for more information. From 74da3cf03a27f39a77f59e2393f1dc97ed22f0b8 Mon Sep 17 00:00:00 2001 From: "Issei.M" Date: Mon, 10 Aug 2015 00:39:06 +0900 Subject: [PATCH 0418/2667] updated validation.rst --- book/validation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/validation.rst b/book/validation.rst index 5f250721c1a..ac03fc8f5fc 100644 --- a/book/validation.rst +++ b/book/validation.rst @@ -212,7 +212,7 @@ The ``validator`` service can be used at any time to validate any object. In reality, however, you'll usually work with the ``validator`` indirectly when working with forms. Symfony's form library uses the ``validator`` service internally to validate the underlying object after values have been submitted. -The constraint violations on the object are converted into ``FieldError`` +The constraint violations on the object are converted into ``FormError`` objects that can easily be displayed with your form. The typical form submission workflow looks like the following from inside a controller:: From d7f0a398f821fa69092153c23628f5e62751d0c5 Mon Sep 17 00:00:00 2001 From: Sebastian Bergmann Date: Sun, 9 Aug 2015 08:45:21 +0200 Subject: [PATCH 0419/2667] Fix --- components/var_dumper/advanced.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/var_dumper/advanced.rst b/components/var_dumper/advanced.rst index 805a3847bd0..ccc0308cad7 100644 --- a/components/var_dumper/advanced.rst +++ b/components/var_dumper/advanced.rst @@ -145,7 +145,7 @@ Another option for doing the same could be:: use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; - cloner = new VarCloner(); + $cloner = new VarCloner(); $dumper = new CliDumper(); $output = fopen('php://memory', 'r+b'); From 478023cfcee1e016a51080ceafe008c66fd4d8e5 Mon Sep 17 00:00:00 2001 From: Alex Wybraniec Date: Mon, 3 Aug 2015 16:14:07 +0100 Subject: [PATCH 0420/2667] Missing --no-interaction flag? Running php app/console doctrine:generate:entity --entity="AppBundle:Category" --fields="name:string(255)" does not automatically generate the entity. Needs the --no-interaction flag setting. --- book/doctrine.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/doctrine.rst b/book/doctrine.rst index 304edfc43bf..7749a0b76bc 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -904,7 +904,7 @@ you can let Doctrine create the class for you. .. code-block:: bash - $ php app/console doctrine:generate:entity \ + $ php app/console doctrine:generate:entity --no-interaction \ --entity="AppBundle:Category" \ --fields="name:string(255)" From f45c392e6d420dfcd07f304b97b89573f43c1da8 Mon Sep 17 00:00:00 2001 From: Antoine Makdessi Date: Tue, 26 May 2015 10:29:32 +0200 Subject: [PATCH 0421/2667] [Console] Command Lifecycle explications --- components/console/introduction.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index eda67260924..8f68a5c7d66 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -110,6 +110,31 @@ This prints:: HELLO FABIEN +Command Lifecycle +~~~~~~~~~~~~~~~~~ + +Commands have three lifecycle methods: + +:method:`Symfony\\Component\\Console\\Command\\Command::initialize` + This method is executed before the ``interact()`` and the ``execute()`` + methods. It's main purpose is to initialize the variables used in the + rest of the command methods. + +:method:`Symfony\\Component\\Console\\Command\\Command::interact` + This method is executed after ``initialize()`` and before ``execute()``. + Its purpose is to check if some of the options/arguments are missing + and interactively ask the user for those values. This is the last place + where you can ask for missing options/arguments otherwise the command + will throw an error. + +:method:`Symfony\\Component\\Console\\Command\\Command::execute` + This method is executed after ``interact()`` and ``initialize()``. + It contains the logic you want the command executes. + +Note that ``execute()`` is the only required method of the three. + +The ``initialize()`` and ``interact()`` methods are completely optional. + .. _components-console-coloring: Coloring the Output From 01965ccdef4a79a8d13ead3069e11c49ab57b594 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Fri, 14 Aug 2015 11:43:24 +0200 Subject: [PATCH 0422/2667] [#5319] Applying comments and minor tweaks --- components/console/introduction.rst | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 01830f42d8b..5e6684c3c7e 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -117,25 +117,21 @@ Command Lifecycle Commands have three lifecycle methods: -:method:`Symfony\\Component\\Console\\Command\\Command::initialize` +:method:`Symfony\\Component\\Console\\Command\\Command::initialize` *(optional)* This method is executed before the ``interact()`` and the ``execute()`` - methods. It's main purpose is to initialize the variables used in the - rest of the command methods. + methods. Its main purpose is to initialize variables used in the rest of + the command methods. -:method:`Symfony\\Component\\Console\\Command\\Command::interact` +:method:`Symfony\\Component\\Console\\Command\\Command::interact` *(optional)* This method is executed after ``initialize()`` and before ``execute()``. Its purpose is to check if some of the options/arguments are missing and interactively ask the user for those values. This is the last place - where you can ask for missing options/arguments otherwise the command - will throw an error. + where you can ask for missing options/arguments. After this command, + missing options/arguments will result in an error. -:method:`Symfony\\Component\\Console\\Command\\Command::execute` +:method:`Symfony\\Component\\Console\\Command\\Command::execute` *(required)* This method is executed after ``interact()`` and ``initialize()``. - It contains the logic you want the command executes. - -Note that ``execute()`` is the only required method of the three. - -The ``initialize()`` and ``interact()`` methods are completely optional. + It contains the logic you want the command to execute. .. _components-console-coloring: From 11fae59727c07337177da33ed15e18a3c0e9be7e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 17 Aug 2015 09:12:55 +0200 Subject: [PATCH 0423/2667] Updated the installation chapter --- book/installation.rst | 13 ++----------- images/quick_tour/welcome.png | Bin 51365 -> 66708 bytes 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/book/installation.rst b/book/installation.rst index 95644e0f7fb..fda0156f658 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -98,9 +98,6 @@ optional second argument of the ``new`` command: $ symfony new my_project_name 2.3.26 $ symfony new my_project_name 2.6.5 - # use the most recent LTS (Long Term Support) version - $ symfony new my_project_name lts - If you want your project to be based on the latest :ref:`Symfony LTS version `, pass ``lts`` as the second argument of the ``new`` command: @@ -169,9 +166,8 @@ browsing the project directory and executing this command: $ cd my_project_name/ $ php app/console server:run -Then, open your browser and access the ``http://localhost:8000/app/example`` -URL to see the -Welcome page of Symfony: +Then, open your browser and access the ``http://localhost:8000/`` URL to see the +Welcome Page of Symfony: .. image:: /images/quick_tour/welcome.png :align: center @@ -398,11 +394,6 @@ need in your new application. Be sure to also check out the :doc:`Cookbook `, which contains a wide variety of articles about solving specific problems with Symfony. -.. note:: - - If you want to remove the sample code from your distribution, take a look - at this cookbook article: ":doc:`/cookbook/bundles/remove`" - .. _`explained in this post`: http://fabien.potencier.org/article/73/signing-project-releases .. _`Composer`: https://getcomposer.org/ .. _`Composer download page`: https://getcomposer.org/download/ diff --git a/images/quick_tour/welcome.png b/images/quick_tour/welcome.png index 1a9c527ff63f418f2da400dbdd73ce0f76831233..7eb0395eb43cd8dc4ab3514355bef2bb7808dba2 100644 GIT binary patch literal 66708 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 literal 51365 zcmeFabx>T*)-Q~c-~@L9K@wyjxI+jKLU4C?8Qg|JLU0f65Q2Mfhu{vub&%jXxDFrB zvG?S>_x^F~lY6T^sG_Rr-K%@;)k}W8dUq3|ASZ>1Mv4Xp2Zt&B;k^;%z&5m{CeAN4)hS9Np&IT^pzE3*M#+Cjz9^A^VjUO8-+&hNqz2Q;Z z;A;vK)0gD5mz=E2KB(D(qUXh>_ZJ$j3MR0J9gv z+x)75k2%wz&z@N^kWpyfDZEtkoiy{Al(+|3S!U<$<~k`gdYh171I7`=NrD0Vm`7llE2>Jyi^OTm9gi!Tw(wI}YK z$b1$SePd9RJhy)TlX;do1Vy!vlCMw^{+9-GWGV&G+p>txb7;I(JFkxSS&|vX2y1z8 z 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�n!V>FcVuz9^ZiTXYBuUIN1dL`mG-tOECb&J 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 F41A 0EJ%u%BZvVPEA5bfTyAb&US4ia4qW5> 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�t{;Ekcm%zrmtFe6lv5VlR~5_H39T8 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 Date: Mon, 17 Aug 2015 14:32:51 +0200 Subject: [PATCH 0424/2667] Fix the way we found the apache/nginx user If you have another web server like tomcat on your server, the previous command end up returning tomcat instead of apache. Previously we looked at the whole line, now only at the user and the command name --- book/installation.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/installation.rst b/book/installation.rst index 95644e0f7fb..e8374ae0fbc 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -243,7 +243,7 @@ If there are any issues, correct them now before moving on. $ rm -rf app/cache/* $ rm -rf app/logs/* - $ HTTPDUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1` + $ HTTPDUSER=`ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1` $ sudo chmod +a "$HTTPDUSER allow delete,write,append,file_inherit,directory_inherit" app/cache app/logs $ sudo chmod +a "`whoami` allow delete,write,append,file_inherit,directory_inherit" app/cache app/logs @@ -258,7 +258,7 @@ If there are any issues, correct them now before moving on. .. code-block:: bash - $ HTTPDUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1` + $ HTTPDUSER=`ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1` $ sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs $ sudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs From 3a0bd9d59ea52af4091def50caa5781740b6d120 Mon Sep 17 00:00:00 2001 From: Mohammed Rhamnia Date: Mon, 17 Aug 2015 19:03:42 +0100 Subject: [PATCH 0425/2667] Add a caution about logout when using http-basic authenticated firewall --- book/security.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/book/security.rst b/book/security.rst index 1d1259d5e90..c177f540cde 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1133,6 +1133,13 @@ is defined by the ``target`` parameter above (e.g. the ``homepage``). :class:`Symfony\\Component\\Security\\Http\\Logout\\LogoutSuccessHandlerInterface`. See :doc:`Security Configuration Reference `. +.. caution:: + + Notice that when using http-basic authenticated firewall there is no real + way to log out : the only way to 'logout' is to have the browser stop sending your name and password + on every request. Clearing your browser cache, restarting your browser usually helps, and some web developer + tools might be helpful here. + .. _`security-encoding-password`: Dynamically Encoding a Password From 80c67b0824a380c9e36841cac463db5e3b59b4f4 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 11 Aug 2015 17:32:10 +0200 Subject: [PATCH 0426/2667] Changed the recommendation about the LICENSE file for third-party bundles --- cookbook/bundles/best_practices.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cookbook/bundles/best_practices.rst b/cookbook/bundles/best_practices.rst index 58f01c74366..cd15f03eb7e 100644 --- a/cookbook/bundles/best_practices.rst +++ b/cookbook/bundles/best_practices.rst @@ -83,9 +83,8 @@ The basic directory structure of an AcmeBlogBundle must read as follows: ├─ AcmeBlogBundle.php ├─ Controller/ ├─ README.md + ├─ LICENSE ├─ Resources/ - │ ├─ meta/ - │ │ └─ LICENSE │ ├─ config/ │ ├─ doc/ │ │ └─ index.rst @@ -102,9 +101,8 @@ that automated tools can rely on: * ``README.md``: This file contains the basic description of the bundle and it usually shows some basic examples and links to its full documentation (it can use any of the markup formats supported by GitHub, such as ``README.rst``); -* ``Resources/meta/LICENSE``: The full license for the code. The license file - can also be stored in the bundle's root directory to follow the generic - conventions about packages; +* ``LICENSE``: The full contents of the license used by the code. Most third-party + bundles are published under the MIT license, but you can `choose any license`_; * ``Resources/doc/index.rst``: The root file for the Bundle documentation. The depth of sub-directories should be kept to the minimum for most used @@ -424,3 +422,4 @@ Learn more from the Cookbook .. _`PSR-4`: http://www.php-fig.org/psr/psr-4/ .. _`Semantic Versioning Standard`: http://semver.org/ .. _`Packagist`: https://packagist.org/ +.. _`choose any license`: http://choosealicense.com/ From 4d1de980eb610cace65526324c5f8808c2edeedc Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 4 Aug 2015 23:31:06 +0200 Subject: [PATCH 0427/2667] proofread comments in voter article --- cookbook/security/voters.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index 8b1ab715e11..3da6cd8253f 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -104,12 +104,12 @@ edit a particular object. Here's an example implementation:: */ public function vote(TokenInterface $token, $post, array $attributes) { - // check if class of this object is supported by this voter + // check if the class of this object is supported by this voter if (!$this->supportsClass(get_class($post))) { return VoterInterface::ACCESS_ABSTAIN; } - // check if the voter is used correct, only allow one attribute + // check if the voter is used correctly, only allow one attribute // this isn't a requirement, it's just one easy way for you to // design your voter if (1 !== count($attributes)) { @@ -134,9 +134,8 @@ edit a particular object. Here's an example implementation:: return VoterInterface::ACCESS_DENIED; } - // double-check that the User object is the expected entity. - // It always will be, unless there is some misconfiguration of the - // security system. + // double-check that the User object is the expected entity (this + // only happens when you did not configure the security system properly) if (!$user instanceof User) { throw new \LogicException('The user is somehow not our User class!'); } @@ -196,7 +195,8 @@ and tag it with ``security.voter``: + public="false" + > @@ -238,7 +238,7 @@ from the security context is called. // get a Post instance $post = ...; - // keep in mind, this will call all registered security voters + // keep in mind that this will call all registered security voters if (false === $this->get('security.context')->isGranted('view', $post)) { throw new AccessDeniedException('Unauthorized access!'); } From df0afdd8a96e2c6ca4bb3b711b4851542d682f1c Mon Sep 17 00:00:00 2001 From: Augustin Delaporte Date: Tue, 18 Aug 2015 13:34:37 +0200 Subject: [PATCH 0428/2667] Upgrade Platform.sh configuration snippet. --- cookbook/deployment/platformsh.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cookbook/deployment/platformsh.rst b/cookbook/deployment/platformsh.rst index de97cd1111b..7b679177feb 100644 --- a/cookbook/deployment/platformsh.rst +++ b/cookbook/deployment/platformsh.rst @@ -37,9 +37,11 @@ Platform.sh how to deploy your application (read more about # The name of this app. Must be unique within a project. name: myphpproject - - # The toolstack used to build the application. - toolstack: "php:symfony" + + # The type of the application to build. + type: php:5.6 + build: + flavor: symfony # The relationships of the application with services or other applications. # The left-hand side is the name of the relationship as it will be exposed From 62fc0dc0b7f014e9ee81e692f9b3fcd9980abf0f Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 19 Aug 2015 09:22:57 +0200 Subject: [PATCH 0429/2667] Update web-assets.rst --- best_practices/web-assets.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/best_practices/web-assets.rst b/best_practices/web-assets.rst index 9d2570e0bd1..dd66ce8e2f6 100644 --- a/best_practices/web-assets.rst +++ b/best_practices/web-assets.rst @@ -30,7 +30,7 @@ much more concise: Keep in mind that ``web/`` is a public directory and that anything stored here will be publicly accessible, including all the original asset files - (e.g. Sass, LESS and CoffeScript files). + (e.g. Sass, LESS and CoffeeScript files). Using Assetic ------------- From 1df9453c2dab72acdddbdaa52d8d0b79aee5b2b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Nadaud?= Date: Wed, 19 Aug 2015 17:39:16 +0200 Subject: [PATCH 0430/2667] Update page_creation.rst --- book/page_creation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/page_creation.rst b/book/page_creation.rst index 37e62d94253..9e649356024 100644 --- a/book/page_creation.rst +++ b/book/page_creation.rst @@ -43,7 +43,7 @@ a method inside of it that will be executed when someone goes to ``/lucky/number use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Component\HttpFoundation\Response; - class LuckyController + class LuckyController extends Controller { /** * @Route("/lucky/number") From dede18324286b72105e30f2ce337369560170215 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 3 Aug 2015 15:47:51 +0200 Subject: [PATCH 0431/2667] Updated the profiler matchers article --- cookbook/profiler/matchers.rst | 59 +++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/cookbook/profiler/matchers.rst b/cookbook/profiler/matchers.rst index 2a990aff333..0bf1ad9ef0e 100644 --- a/cookbook/profiler/matchers.rst +++ b/cookbook/profiler/matchers.rst @@ -4,20 +4,20 @@ How to Use Matchers to Enable the Profiler Conditionally ======================================================== -By default, the profiler is only activated in the development environment. But -it's imaginable that a developer may want to see the profiler even in -production. Another situation may be that you want to show the profiler only -when an admin has logged in. You can enable the profiler in these situations -by using matchers. +The Symfony profiler is only activated in the development environment to not hurt +your application performance. However, sometimes it may be useful to conditionally +enable the profiler in the production environment to assist you in hard to debug +issues. This behavior is implemented with the **Request Matchers**. Using the built-in Matcher -------------------------- -Symfony provides a +A Request Matcher is a class that checks whether a given ``Request`` instance +matches a set of conditions. Symfony provides a :class:`built-in matcher ` -which can match paths and IPs. For example, if you want to only show the -profiler when accessing the page with the ``168.0.0.1`` IP, then you can -use this configuration: +which matches paths and IPs. For example, if you want to only show the profiler +when accessing the page with the ``168.0.0.1`` IP, then you can use this +configuration: .. configuration-block:: @@ -50,22 +50,24 @@ use this configuration: You can also set a ``path`` option to define the path on which the profiler should be enabled. For instance, setting it to ``^/admin/`` will enable the -profiler only for the ``/admin/`` URLs. +profiler only for the URLs which start with ``/admin/``. -Creating a custom Matcher +Creating a Custom Matcher ------------------------- -You can also create a custom matcher. This is a service that checks whether -the profiler should be enabled or not. To create that service, create a class +Leveraging the concept of Request Matchers you can define a custom matcher to +enable the profiler conditionally in your application. To do so, create a class which implements :class:`Symfony\\Component\\HttpFoundation\\RequestMatcherInterface`. This interface requires one method: :method:`Symfony\\Component\\HttpFoundation\\RequestMatcherInterface::matches`. -This method returns false to disable the profiler and true to enable the -profiler. +This method returns ``false`` when the request doesn't match the conditions and +``true`` otherwise. Therefore, the custom matcher must return ``false`` to +disable the profiler and ``true`` to enable it. -To enable the profiler when a ``ROLE_SUPER_ADMIN`` is logged in, you can use -something like:: +Suppose that the profiler must be enabled whenever a user with a +``ROLE_SUPER_ADMIN`` is logged in. This is the only code needed for that custom +matcher:: // src/AppBundle/Profiler/SuperAdminMatcher.php namespace AppBundle\Profiler; @@ -89,7 +91,8 @@ something like:: } } -Then, you need to configure the service: +Then, configure a new service and set it as ``private`` because the application +won't use it directly: .. configuration-block:: @@ -97,16 +100,17 @@ Then, you need to configure the service: # app/config/services.yml services: - app.profiler.matcher.super_admin: + app.super_admin_matcher: class: AppBundle\Profiler\SuperAdminMatcher arguments: ["@security.context"] + public: false .. code-block:: xml - + @@ -116,12 +120,15 @@ Then, you need to configure the service: use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - $container->setDefinition('app.profiler.matcher.super_admin', new Definition( + $definition = new Definition( 'AppBundle\Profiler\SuperAdminMatcher', array(new Reference('security.context')) ); + $definition->setPublic(false); -Now the service is registered, the only thing left to do is configure the + $container->setDefinition('app.super_admin_matcher', $definition); + +Once the service is registered, the only thing left to do is configure the profiler to use this service as the matcher: .. configuration-block:: @@ -133,7 +140,7 @@ profiler to use this service as the matcher: # ... profiler: matcher: - service: app.profiler.matcher.super_admin + service: app.super_admin_matcher .. code-block:: xml @@ -141,7 +148,7 @@ profiler to use this service as the matcher: @@ -151,6 +158,6 @@ profiler to use this service as the matcher: $container->loadFromExtension('framework', array( // ... 'profiler' => array( - 'service' => 'app.profiler.matcher.super_admin', + 'service' => 'app.super_admin_matcher', ), )); From 6c76b2bf0f04ca39a2ba524ac50d781a6da05aca Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 20 Aug 2015 11:06:52 +0200 Subject: [PATCH 0432/2667] [#5593] Very little rewording and improving config --- cookbook/profiler/matchers.rst | 46 ++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/cookbook/profiler/matchers.rst b/cookbook/profiler/matchers.rst index 0bf1ad9ef0e..0d7c1a4f2b8 100644 --- a/cookbook/profiler/matchers.rst +++ b/cookbook/profiler/matchers.rst @@ -6,13 +6,13 @@ How to Use Matchers to Enable the Profiler Conditionally The Symfony profiler is only activated in the development environment to not hurt your application performance. However, sometimes it may be useful to conditionally -enable the profiler in the production environment to assist you in hard to debug +enable the profiler in the production environment to assist you in debugging issues. This behavior is implemented with the **Request Matchers**. Using the built-in Matcher -------------------------- -A Request Matcher is a class that checks whether a given ``Request`` instance +A request matcher is a class that checks whether a given ``Request`` instance matches a set of conditions. Symfony provides a :class:`built-in matcher ` which matches paths and IPs. For example, if you want to only show the profiler @@ -33,16 +33,27 @@ configuration: .. code-block:: xml - - - + + + + + + + + .. code-block:: php // app/config/config.php $container->loadFromExtension('framework', array( + // ... 'profiler' => array( 'ip' => '168.0.0.1', ), @@ -145,12 +156,21 @@ profiler to use this service as the matcher: .. code-block:: xml - - - - + + + + + + + + .. code-block:: php From a4f5b0841591fee534082f9dd0a16be6c57269ea Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 20 Aug 2015 11:23:46 +0200 Subject: [PATCH 0433/2667] Move important information out of versionadded --- components/console/helpers/progressbar.rst | 6 +++++- cookbook/controller/service.rst | 10 +++++++--- reference/twig_reference.rst | 9 +++++---- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/components/console/helpers/progressbar.rst b/components/console/helpers/progressbar.rst index 0e7900b4df0..3c97a53ec49 100644 --- a/components/console/helpers/progressbar.rst +++ b/components/console/helpers/progressbar.rst @@ -48,7 +48,8 @@ you can also set the current progress by calling the Prior to version 2.6, the progress bar only works if your platform supports ANSI codes; on other platforms, no output is generated. -.. versionadded:: 2.6 +.. tip:: + If your platform doesn't support ANSI codes, updates to the progress bar are added as new lines. To prevent the output from being flooded, adjust the @@ -56,6 +57,9 @@ you can also set the current progress by calling the accordingly. By default, when using a ``max``, the redraw frequency is set to *10%* of your ``max``. + .. versionadded:: 2.6 + The ``setRedrawFrequency()`` method was introduced in Symfony 2.6. + If you don't know the number of steps in advance, just omit the steps argument when creating the :class:`Symfony\\Component\\Console\\Helper\\ProgressBar` instance:: diff --git a/cookbook/controller/service.rst b/cookbook/controller/service.rst index 94e4202f2a1..f8f2d24da30 100644 --- a/cookbook/controller/service.rst +++ b/cookbook/controller/service.rst @@ -122,9 +122,13 @@ the route ``_controller`` value: defined as a service. See the `FrameworkExtraBundle documentation`_ for details. -.. versionadded:: 2.6 - If your controller service implements the ``__invoke`` method, you can simply refer to the service id - (``app.hello_controller``). +.. tip:: + + If your controller implements the ``__invoke()`` method, you can simply + refer to the service id (``app.hello_controller``). + + .. versionadded:: 2.6 + Support for ``__invoke()`` was introduced in Symfony 2.6. Alternatives to base Controller Methods --------------------------------------- diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 55e55195f2c..d5224d69915 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -719,11 +719,12 @@ The available attributes are: * ``app.session`` * ``app.environment`` * ``app.debug`` -* ``app.security`` +* ``app.security`` (deprecated as of 2.6) -.. versionadded:: 2.6 - The ``app.security`` global is deprecated as of 2.6. The user is already available - as ``app.user`` and ``is_granted()`` is registered as function. +.. caution:: + + The ``app.security`` global is deprecated as of 2.6. The user is already + available as ``app.user`` and ``is_granted()`` is registered as function. Symfony Standard Edition Extensions ----------------------------------- From f33868bbe2eecde2e71873fecac8e2f27edfe061 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Wed, 29 Jul 2015 14:05:18 +0200 Subject: [PATCH 0434/2667] Improve Platform.sh mention in contributing docs --- contributing/documentation/overview.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/contributing/documentation/overview.rst b/contributing/documentation/overview.rst index fd101ae7d08..378bbd9aeb6 100644 --- a/contributing/documentation/overview.rst +++ b/contributing/documentation/overview.rst @@ -269,6 +269,11 @@ page on GitHub and click on ``Details``. ``.platform.app.yaml``, ``.platform/services.yaml`` and ``.platform/routes.yaml`` allow `Platform.sh`_ to build Pull Requests. +.. note:: + + Only Pull Requests to maintained branches are automatically built by + Platform.sh. Check the `roadmap`_ for maintained branches. + Minor Changes (e.g. Typos) -------------------------- @@ -345,4 +350,5 @@ definitely don't want you to waste your time! .. _SensioLabsConnect: https://connect.sensiolabs.com/ .. _`Symfony Documentation Badge`: https://connect.sensiolabs.com/badge/36/symfony-documentation-contributor .. _`sync your fork`: https://help.github.com/articles/syncing-a-fork -.. _`Platform.sh`: https://platform.sh \ No newline at end of file +.. _`Platform.sh`: https://platform.sh +.. _`roadmap`: https://symfony.com/roadmap From 1c8cfbe92cc9bda97f41ea215549e12b7eecefb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Nadaud?= Date: Thu, 20 Aug 2015 13:18:13 +0200 Subject: [PATCH 0435/2667] Update page_creation.rst | Q | A | ------------- | --- | Doc fix? | yes | New docs? | no | Applies to | 2.7 | Fixed tickets | --- book/page_creation.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/book/page_creation.rst b/book/page_creation.rst index 9e649356024..e317c75deff 100644 --- a/book/page_creation.rst +++ b/book/page_creation.rst @@ -104,7 +104,7 @@ Just add a second method to ``LuckyController``:: // src/AppBundle/Controller/LuckyController.php // ... - class LuckyController + class LuckyController extends Controller { // ... @@ -137,7 +137,7 @@ You can even shorten this with the handy :class:`Symfony\\Component\\HttpFoundat // --> don't forget this new use statement use Symfony\Component\HttpFoundation\JsonResponse; - class LuckyController + class LuckyController extends Controller { // ... @@ -170,7 +170,7 @@ at the end: // src/AppBundle/Controller/LuckyController.php // ... - class LuckyController + class LuckyController extends Controller { /** * @Route("/lucky/number/{count}") @@ -224,7 +224,7 @@ The best part is that you can access this value and use it in your controller:: // src/AppBundle/Controller/LuckyController.php // ... - class LuckyController + class LuckyController extends Controller { /** From ce8110c6a864f65336db8ccbf4bdb71cf0677326 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 20 Aug 2015 13:11:46 +0200 Subject: [PATCH 0436/2667] Improve docs on customizing prototype rendering --- cookbook/form/form_collections.rst | 77 +++------------------------- cookbook/form/form_customization.rst | 43 ++++++++++++++++ 2 files changed, 49 insertions(+), 71 deletions(-) diff --git a/cookbook/form/form_collections.rst b/cookbook/form/form_collections.rst index 8511844e855..a02853ea97e 100644 --- a/cookbook/form/form_collections.rst +++ b/cookbook/form/form_collections.rst @@ -262,7 +262,7 @@ great, your user can't actually add any new tags yet. .. _cookbook-form-collections-new-prototype: Allowing "new" Tags with the "Prototype" ------------------------------------------ +---------------------------------------- Allowing the user to dynamically add new tags means that you'll need to use some JavaScript. Previously you added two tags to your form in the controller. @@ -417,6 +417,11 @@ into new ``Tag`` objects and added to the ``tags`` property of the ``Task`` obje You can find a working example in this `JSFiddle`_. +.. seealso:: + + If you want to customize the HTML code in the prototype, read + :ref:`cookbook-form-custom-prototype`. + To make handling these new tags easier, add an "adder" and a "remover" method for the tags in the ``Task`` class:: @@ -729,75 +734,5 @@ the relationship between the removed ``Tag`` and ``Task`` object. updated (whether you're adding new tags or removing existing tags) on each Tag object itself. -.. _cookbook-form-collections-custom-prototype: - -Rendering a Custom Prototype ----------------------------- - -Most of the time the provided prototype will be sufficient for your needs -and does not need to be changed. But if you are in the situation were you -need to have a complete custom prototype, you can render it yourself. - -The Form component automatically looks for a block whose name follows a certain -schema to decide how to render each entry of the form type collection. For -example, if your form field is named ``tasks``, you will be able to change -the widget for each task as follows: - -.. configuration-block:: - - .. code-block:: html+jinja - - {% form_theme form _self %} - - {% block _tasks_entry_widget %} - - {{ form_widget(task.task) }} - {{ form_widget(task.dueDate) }} - - {% endblock %} - - .. code-block:: html+php - - - - widget($form->task) ?> - widget($form->dueDate) ?> - - -Not only can you override the rendered widget, but you can also change the -complete form row or the label as well. For the ``tasks`` field given above, -the block names would be the following: - -================ ======================= -Part of the Form Block Name -================ ======================= -``label`` ``_tasks_entry_label`` -``widget`` ``_tasks_entry_widget`` -``row`` ``_tasks_entry_row`` -================ ======================= - -Then, you only have to ensure to render the collection type's ``data-prototype`` -property with the proper prototype so that new entries will be rendered the -same way as existing ones: - -.. configuration-block:: - - .. code-block:: html+jinja - - {% form_theme form _self %} - - {% block _tasks_widget %} - {% set attr = attr|merge({ 'data-prototype': form_row(prototype) }) %} - - {% for child in form %} - {{ form_row(child) }} - {% endfor %} -
- {% endblock %} - - .. code-block:: html+php - - - .. _`Owning Side and Inverse Side`: http://docs.doctrine-project.org/en/latest/reference/unitofwork-associations.html .. _`JSFiddle`: http://jsfiddle.net/847Kf/4/ diff --git a/cookbook/form/form_customization.rst b/cookbook/form/form_customization.rst index 21a34f1b346..c6265a7528a 100644 --- a/cookbook/form/form_customization.rst +++ b/cookbook/form/form_customization.rst @@ -734,6 +734,49 @@ You can also override the markup for an entire field row using the same method: widget($form) ?> +.. _cookbook-form-custom-prototype: + +How to Customize a Collection Prototype +--------------------------------------- + +When using a :doc:`collection of forms `, +the prototype can be overridden with a completely custom prototype by +overriding a block. For example, if your form field is named ``tasks``, you +will be able to change the widget for each task as follows: + +.. configuration-block:: + + .. code-block:: html+jinja + + {% form_theme form _self %} + + {% block _tasks_entry_widget %} + + {{ form_widget(task.task) }} + {{ form_widget(task.dueDate) }} + + {% endblock %} + + .. code-block:: html+php + + + + widget($form->task) ?> + widget($form->dueDate) ?> + + +Not only can you override the rendered widget, but you can also change the +complete form row or the label as well. For the ``tasks`` field given above, +the block names would be the following: + +================ ======================= +Part of the Form Block Name +================ ======================= +``label`` ``_tasks_entry_label`` +``widget`` ``_tasks_entry_widget`` +``row`` ``_tasks_entry_row`` +================ ======================= + Other common Customizations --------------------------- From fe7aa8bcfb9a9ab56efc0e44caffddec072306a3 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 20 Aug 2015 23:26:38 +0200 Subject: [PATCH 0437/2667] Updated Constraint reference with best practices --- reference/constraints/All.rst | 16 +++--- reference/constraints/Blank.rst | 16 +++--- reference/constraints/Callback.rst | 42 +++++++-------- reference/constraints/CardScheme.rst | 16 +++--- reference/constraints/Choice.rst | 52 +++++++++---------- reference/constraints/Collection.rst | 36 ++++++------- reference/constraints/Count.rst | 16 +++--- reference/constraints/Country.rst | 16 +++--- reference/constraints/Currency.rst | 16 +++--- reference/constraints/Date.rst | 16 +++--- reference/constraints/DateTime.rst | 16 +++--- reference/constraints/Email.rst | 16 +++--- reference/constraints/EqualTo.rst | 16 +++--- reference/constraints/False.rst | 16 +++--- reference/constraints/File.rst | 20 ++++---- reference/constraints/GreaterThan.rst | 16 +++--- reference/constraints/GreaterThanOrEqual.rst | 16 +++--- reference/constraints/Iban.rst | 16 +++--- reference/constraints/IdenticalTo.rst | 16 +++--- reference/constraints/Image.rst | 20 ++++---- reference/constraints/Ip.rst | 16 +++--- reference/constraints/Isbn.rst | 16 +++--- reference/constraints/Issn.rst | 16 +++--- reference/constraints/Language.rst | 16 +++--- reference/constraints/Length.rst | 16 +++--- reference/constraints/LessThan.rst | 16 +++--- reference/constraints/LessThanOrEqual.rst | 16 +++--- reference/constraints/Locale.rst | 16 +++--- reference/constraints/Luhn.rst | 16 +++--- reference/constraints/NotBlank.rst | 16 +++--- reference/constraints/NotEqualTo.rst | 16 +++--- reference/constraints/NotIdenticalTo.rst | 16 +++--- reference/constraints/NotNull.rst | 16 +++--- reference/constraints/Null.rst | 16 +++--- reference/constraints/Range.rst | 16 +++--- reference/constraints/Regex.rst | 48 ++++++++--------- reference/constraints/Time.rst | 16 +++--- reference/constraints/True.rst | 18 +++---- reference/constraints/Type.rst | 16 +++--- reference/constraints/UniqueEntity.rst | 34 ++++++------ reference/constraints/Url.rst | 48 ++++++++--------- reference/constraints/UserPassword.rst | 16 +++--- reference/constraints/Valid.rst | 54 ++++++++++---------- 43 files changed, 450 insertions(+), 450 deletions(-) diff --git a/reference/constraints/All.rst b/reference/constraints/All.rst index 34a59dfc9fe..157827df0cf 100644 --- a/reference/constraints/All.rst +++ b/reference/constraints/All.rst @@ -24,8 +24,8 @@ entry in that array: .. code-block:: php-annotations - // src/Acme/UserBundle/Entity/User.php - namespace Acme\UserBundle\Entity; + // src/AppBundle/Entity/User.php + namespace AppBundle\Entity; use Symfony\Component\Validator\Constraints as Assert; @@ -42,8 +42,8 @@ entry in that array: .. code-block:: yaml - # src/Acme/UserBundle/Resources/config/validation.yml - Acme\UserBundle\Entity\User: + # src/AppBundle/Resources/config/validation.yml + AppBundle\Entity\User: properties: favoriteColors: - All: @@ -53,13 +53,13 @@ entry in that array: .. code-block:: xml - + - +
diff --git a/.platform/_templates/layout.html b/.platform/_templates/layout.html new file mode 100644 index 00000000000..3264dbaf2dc --- /dev/null +++ b/.platform/_templates/layout.html @@ -0,0 +1,72 @@ +{% extends '!layout.html' %} + +{% set css_files = ['http://symfony.com/css/compiled/v5/all.css?v=4'] %} +{# make sure the Sphinx stylesheet isn't loaded #} +{% set style = '' %} +{% set isIndex = pagename is index %} + +{% block extrahead %} +{# add JS to support tabs #} + + +{# pygment's styles are still loaded, undo some unwanted styles #} + +{% endblock %} + +{% block header %} +{# ugly way, now we have 2 body tags, but styles rely on these classes #} + +{% endblock %} + +{% block content %} +
+
+ {%- if render_sidebar %} + + {%- endif %} + +
+ + +

{{ title }}

+ +
+ {% block body %}{% endblock %} +
+ + {% if prev and next %} + + {% endif %} +
+
+
+{% endblock %} + +{# relbar1 is at the top and should not render the quick navigation #} +{% block relbar1 %}{% endblock %} +{% block relbar2 %}{% endblock %} + +{# remove "generated by sphinx" footer #} +{% block footer %}{% endblock %} diff --git a/.platform/_templates/localtoc.html b/.platform/_templates/localtoc.html new file mode 100644 index 00000000000..0ffea6e1ecd --- /dev/null +++ b/.platform/_templates/localtoc.html @@ -0,0 +1,6 @@ +
+

{{ _('Table Of Contents') }}

+
+ {{ toc }} +
+
diff --git a/conf.py b/conf.py index ab4e823e384..db9676a43ed 100644 --- a/conf.py +++ b/conf.py @@ -19,6 +19,7 @@ #sys.path.insert(0, os.path.abspath('.')) sys.path.append(os.path.abspath('_exts')) +sys.path.append(os.path.abspath('.platform/_exts')) # adding PhpLexer from sphinx.highlighting import lexers @@ -34,11 +35,14 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', - 'sensio.sphinx.refinclude', 'sensio.sphinx.configurationblock', 'sensio.sphinx.phpcode', 'sensio.sphinx.bestpractice'] +extensions = [ + 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', + 'sensio.sphinx.refinclude', 'sensio.sphinx.configurationblock', 'sensio.sphinx.phpcode', 'sensio.sphinx.bestpractice', 'sensio.sphinx.codeblock', + 'symfonycom.sphinx' +] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ['.platform/_templates'] # The suffix of source filenames. source_suffix = '.rst' From 3e829456c526463bb00a4821f0bc2cfddc99f476 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Tue, 18 Aug 2015 13:10:30 +0200 Subject: [PATCH 0439/2667] Temporary disable codeblock --- conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf.py b/conf.py index db9676a43ed..bff5cbee037 100644 --- a/conf.py +++ b/conf.py @@ -37,7 +37,7 @@ # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', - 'sensio.sphinx.refinclude', 'sensio.sphinx.configurationblock', 'sensio.sphinx.phpcode', 'sensio.sphinx.bestpractice', 'sensio.sphinx.codeblock', + 'sensio.sphinx.refinclude', 'sensio.sphinx.configurationblock', 'sensio.sphinx.phpcode', 'sensio.sphinx.bestpractice', #'sensio.sphinx.codeblock', 'symfonycom.sphinx' ] From bafc2ec776065eb4e651d005d350734089123c3f Mon Sep 17 00:00:00 2001 From: Wouter J Date: Tue, 18 Aug 2015 13:16:17 +0200 Subject: [PATCH 0440/2667] Use a more recent version of Sphinx --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 667ca86fa50..3ad82398f25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ cache: - $HOME/.cache/pip - _build -install: pip install sphinx==1.1.3 +install: pip install sphinx~=1.3 script: sphinx-build -nW -b html -d _build/doctrees . _build/html From f931d83258f02f9200e731c1f50e30d171f00f4d Mon Sep 17 00:00:00 2001 From: Wouter J Date: Tue, 18 Aug 2015 13:19:43 +0200 Subject: [PATCH 0441/2667] Update config --- conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf.py b/conf.py index bff5cbee037..c9f5ce633ac 100644 --- a/conf.py +++ b/conf.py @@ -130,7 +130,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = 'classic' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the From a10683b37798e6a4b7ad46838937f5572052dab2 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 18 Aug 2015 18:57:11 +0200 Subject: [PATCH 0442/2667] Move Sphinx files to _theme --- {.platform => _theme}/_exts/symfonycom/__init__.py | 0 {.platform => _theme}/_exts/symfonycom/sphinx/__init__.py | 0 {.platform => _theme}/_templates/globaltoc.html | 0 {.platform => _theme}/_templates/layout.html | 0 {.platform => _theme}/_templates/localtoc.html | 0 conf.py | 6 +++--- 6 files changed, 3 insertions(+), 3 deletions(-) rename {.platform => _theme}/_exts/symfonycom/__init__.py (100%) rename {.platform => _theme}/_exts/symfonycom/sphinx/__init__.py (100%) rename {.platform => _theme}/_templates/globaltoc.html (100%) rename {.platform => _theme}/_templates/layout.html (100%) rename {.platform => _theme}/_templates/localtoc.html (100%) diff --git a/.platform/_exts/symfonycom/__init__.py b/_theme/_exts/symfonycom/__init__.py similarity index 100% rename from .platform/_exts/symfonycom/__init__.py rename to _theme/_exts/symfonycom/__init__.py diff --git a/.platform/_exts/symfonycom/sphinx/__init__.py b/_theme/_exts/symfonycom/sphinx/__init__.py similarity index 100% rename from .platform/_exts/symfonycom/sphinx/__init__.py rename to _theme/_exts/symfonycom/sphinx/__init__.py diff --git a/.platform/_templates/globaltoc.html b/_theme/_templates/globaltoc.html similarity index 100% rename from .platform/_templates/globaltoc.html rename to _theme/_templates/globaltoc.html diff --git a/.platform/_templates/layout.html b/_theme/_templates/layout.html similarity index 100% rename from .platform/_templates/layout.html rename to _theme/_templates/layout.html diff --git a/.platform/_templates/localtoc.html b/_theme/_templates/localtoc.html similarity index 100% rename from .platform/_templates/localtoc.html rename to _theme/_templates/localtoc.html diff --git a/conf.py b/conf.py index c9f5ce633ac..797ee53f18f 100644 --- a/conf.py +++ b/conf.py @@ -19,7 +19,7 @@ #sys.path.insert(0, os.path.abspath('.')) sys.path.append(os.path.abspath('_exts')) -sys.path.append(os.path.abspath('.platform/_exts')) +sys.path.append(os.path.abspath('_theme/_exts')) # adding PhpLexer from sphinx.highlighting import lexers @@ -42,7 +42,7 @@ ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['.platform/_templates'] +templates_path = ['_theme/_templates'] # The suffix of source filenames. source_suffix = '.rst' @@ -78,7 +78,7 @@ # List of 10000 patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -# exclude_patterns = ['_build', 'bundles'] +exclude_patterns = ['_theme'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None From 01dd675da446051939f73d885041f0841d6cecd9 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Wed, 19 Aug 2015 12:26:04 +0200 Subject: [PATCH 0443/2667] Add demo warning --- _theme/_templates/layout.html | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/_theme/_templates/layout.html b/_theme/_templates/layout.html index 3264dbaf2dc..2696efbeeed 100644 --- a/_theme/_templates/layout.html +++ b/_theme/_templates/layout.html @@ -13,6 +13,13 @@ {% endblock %} @@ -27,11 +34,17 @@ {%- if render_sidebar %} {%- endif %} @@ -59,6 +72,10 @@

{{ title }}

{{ next.title|striptags|e }} »
{% endif %} + +
+

This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.

+
From c70e1328b3f6e59de3c0624c38336847620e166d Mon Sep 17 00:00:00 2001 From: WouterJ Date: Wed, 19 Aug 2015 22:32:27 +0200 Subject: [PATCH 0444/2667] Use numbered code block --- _exts | 2 +- conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/_exts b/_exts index 52f7bd2216c..1b7c4b794d7 160000 --- a/_exts +++ b/_exts @@ -1 +1 @@ -Subproject commit 52f7bd2216cc22ef52494f346c5643bb2a74513f +Subproject commit 1b7c4b794d75f7b0eb6749cadccc58f87e3158a0 diff --git a/conf.py b/conf.py index 797ee53f18f..9e846b32f84 100644 --- a/conf.py +++ b/conf.py @@ -37,7 +37,7 @@ # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', - 'sensio.sphinx.refinclude', 'sensio.sphinx.configurationblock', 'sensio.sphinx.phpcode', 'sensio.sphinx.bestpractice', #'sensio.sphinx.codeblock', + 'sensio.sphinx.refinclude', 'sensio.sphinx.configurationblock', 'sensio.sphinx.phpcode', 'sensio.sphinx.bestpractice', 'sensio.sphinx.codeblock', 'symfonycom.sphinx' ] From 62a99b3a733f3d5a67231e7f9735cb72e0f0cb05 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Wed, 19 Aug 2015 23:13:31 +0200 Subject: [PATCH 0445/2667] Use pip instead of submodules --- .gitmodules | 3 --- .travis.yml | 2 +- _exts | 1 - conf.py | 3 --- 4 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 .gitmodules delete mode 160000 _exts diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 486727b665d..00000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "sphinx-php"] - path = _exts - url = http://github.com/fabpot/sphinx-php diff --git a/.travis.yml b/.travis.yml index 3ad82398f25..29682488140 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ cache: - $HOME/.cache/pip - _build -install: pip install sphinx~=1.3 +install: pip install sphinx~=1.3 git+https://github.com/fabpot/sphinx-php.git script: sphinx-build -nW -b html -d _build/doctrees . _build/html diff --git a/_exts b/_exts deleted file mode 160000 index 1b7c4b794d7..00000000000 --- a/_exts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1b7c4b794d75f7b0eb6749cadccc58f87e3158a0 diff --git a/conf.py b/conf.py index 9e846b32f84..e7c6b9d1800 100644 --- a/conf.py +++ b/conf.py @@ -16,9 +16,6 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -sys.path.append(os.path.abspath('_exts')) sys.path.append(os.path.abspath('_theme/_exts')) # adding PhpLexer From ebc0e0b98fa33465fc6d8456ee53931273fa857b Mon Sep 17 00:00:00 2001 From: WouterJ Date: Wed, 19 Aug 2015 23:24:48 +0200 Subject: [PATCH 0446/2667] Reset some more pygments styles --- _theme/_templates/layout.html | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/_theme/_templates/layout.html b/_theme/_templates/layout.html index 2696efbeeed..01bce31b749 100644 --- a/_theme/_templates/layout.html +++ b/_theme/_templates/layout.html @@ -11,7 +11,16 @@ {# pygment's styles are still loaded, undo some unwanted styles #}