Commit 1798d08f authored by Igor Wiedler's avatar Igor Wiedler

Merge branch 'master' into swiftmailer-test

* master:
  made monolog logger as the default logger when the monolog bridge is installed
  added the ServiceProviderInterface::boot() method
  fixed some skipped comments
  fixed Twig provider
  removed the Symfony bridges provider as it is not needed anymore with Composer
  added the possibility to register a logger for internal services
  fixed markup in the docs
  removed translation.messages in favor of translation.domains
  Remove dependency on the ClassLoader component.
parents 5ed7e45f 410c1ddd
{
"hash": "d22b382f0e20509713b06e6991c82e07",
"hash": "142455bfe45755d318953c04880d43f2",
"packages": [
{
"package": "pimple/pimple",
......@@ -25,11 +25,6 @@
"version": "dev-master",
"source-reference": "6ab1b5f07dd972d2c5e3b5d48c9776d0823149c7"
},
{
"package": "symfony/class-loader",
"version": "dev-master",
"source-reference": "c57e62c886899f8d88264efad23c857eb198dc09"
},
{
"package": "symfony/class-loader",
"version": "dev-master",
......@@ -88,14 +83,14 @@
{
"package": "symfony/http-foundation",
"version": "dev-master",
"source-reference": "5b1581418381f46679604fd19efc8b518f2390ae",
"alias-pretty-version": "2.1.x-dev",
"alias-version": "2.1.9999999.9999999-dev"
"source-reference": "5b1581418381f46679604fd19efc8b518f2390ae"
},
{
"package": "symfony/http-foundation",
"version": "dev-master",
"source-reference": "5b1581418381f46679604fd19efc8b518f2390ae"
"source-reference": "5b1581418381f46679604fd19efc8b518f2390ae",
"alias-pretty-version": "2.1.x-dev",
"alias-version": "2.1.9999999.9999999-dev"
},
{
"package": "symfony/http-kernel",
......@@ -124,21 +119,28 @@
{
"package": "symfony/routing",
"version": "dev-master",
"source-reference": "aec133671d79d530d09aa0b5ca4cea9e4ee4b274"
"source-reference": "aec133671d79d530d09aa0b5ca4cea9e4ee4b274",
"alias-pretty-version": "2.1.x-dev",
"alias-version": "2.1.9999999.9999999-dev"
},
{
"package": "symfony/routing",
"version": "dev-master",
"source-reference": "aec133671d79d530d09aa0b5ca4cea9e4ee4b274",
"alias-pretty-version": "2.1.x-dev",
"alias-version": "2.1.9999999.9999999-dev"
"source-reference": "aec133671d79d530d09aa0b5ca4cea9e4ee4b274"
}
],
"packages-dev": [
{
"package": "doctrine/common",
"version": "2.2.x-dev",
"source-reference": "1e0aa60d109c630d19543d999f12e2852ef8f932"
"version": "dev-master",
"source-reference": "8b403cde97eaede30bd79acab4f18895fd5bdf27",
"alias-pretty-version": "2.3.x-dev",
"alias-version": "2.3.9999999.9999999-dev"
},
{
"package": "doctrine/common",
"version": "dev-master",
"source-reference": "8b403cde97eaede30bd79acab4f18895fd5bdf27"
},
{
"package": "doctrine/dbal",
......@@ -147,20 +149,19 @@
},
{
"package": "monolog/monolog",
"version": "dev-master",
"source-reference": "2eb0c0978d290a1c45346a1955188929cb4e5db7"
"version": "1.1.0"
},
{
"package": "symfony/form",
"version": "dev-master",
"source-reference": "f410a9e3440b1248f8941d332d9e65b80e573ec5",
"alias-pretty-version": "2.1.x-dev",
"alias-version": "2.1.9999999.9999999-dev"
"source-reference": "f410a9e3440b1248f8941d332d9e65b80e573ec5"
},
{
"package": "symfony/form",
"version": "dev-master",
"source-reference": "f410a9e3440b1248f8941d332d9e65b80e573ec5"
"source-reference": "f410a9e3440b1248f8941d332d9e65b80e573ec5",
"alias-pretty-version": "2.1.x-dev",
"alias-version": "2.1.9999999.9999999-dev"
},
{
"package": "symfony/locale",
......@@ -175,9 +176,14 @@
"source-reference": "6a9e36fef0eedcd52b0dde361faab38f4615fe48"
},
{
"package": "symfony/options-resolver",
"package": "symfony/monolog-bridge",
"version": "dev-master",
"source-reference": "92c19fb262283a348093bbd32b5c5dfef8f00612",
"source-reference": "7ef27aa6b88447989fe5da551c2d2746d4cf60aa"
},
{
"package": "symfony/monolog-bridge",
"version": "dev-master",
"source-reference": "7ef27aa6b88447989fe5da551c2d2746d4cf60aa",
"alias-pretty-version": "2.1.x-dev",
"alias-version": "2.1.9999999.9999999-dev"
},
......@@ -187,9 +193,11 @@
"source-reference": "92c19fb262283a348093bbd32b5c5dfef8f00612"
},
{
"package": "symfony/translation",
"package": "symfony/options-resolver",
"version": "dev-master",
"source-reference": "f89af1b91bbb9ee9151e3516aeaf1eda7992a369"
"source-reference": "92c19fb262283a348093bbd32b5c5dfef8f00612",
"alias-pretty-version": "2.1.x-dev",
"alias-version": "2.1.9999999.9999999-dev"
},
{
"package": "symfony/translation",
......@@ -198,6 +206,11 @@
"alias-pretty-version": "2.1.x-dev",
"alias-version": "2.1.9999999.9999999-dev"
},
{
"package": "symfony/translation",
"version": "dev-master",
"source-reference": "f89af1b91bbb9ee9151e3516aeaf1eda7992a369"
},
{
"package": "symfony/twig-bridge",
"version": "dev-master",
......@@ -225,7 +238,7 @@
{
"package": "twig/twig",
"version": "dev-master",
"source-reference": "e28663efd83d76ee36bf9e8a790f75bd880abbf2",
"source-reference": "45d0f6fe67e2199d1148cf1c7e832674af4e8e93",
"alias-pretty-version": "1.8.x-dev",
"alias-version": "1.8.9999999.9999999-dev"
},
......@@ -239,7 +252,7 @@
{
"package": "twig/twig",
"version": "dev-master",
"source-reference": "e28663efd83d76ee36bf9e8a790f75bd880abbf2"
"source-reference": "45d0f6fe67e2199d1148cf1c7e832674af4e8e93"
}
],
"aliases": [
......
......@@ -3,6 +3,13 @@ Changelog
This changelog references all backward incompatibilities as we introduce them:
* **2012-05-26**: added ``boot()`` to ``ServiceProviderInterface``
* **2012-05-26**: Removed ``SymfonyBridgesServiceProvider``
* **2012-05-26**: Removed the ``translator.messages`` parameter (use
``translator.domains`` instead).
* **2012-05-24**: Removed the ``autoloader`` service (use composer instead).
The ``*.class_path`` settings on all the built-in providers have also been
removed in favor of Composer.
......
......@@ -4,31 +4,32 @@ Disable CSRF Protection on a form using the FormExtension
The *FormExtension* provides a service for building form in your application
with the Symfony2 Form component. By default, the *FormExtension* uses the
CSRF Protection avoiding Cross-site request forgery, a method by which a
malicious user attempts to make your legitimate users unknowingly submit
data that they don't intend to submit.
malicious user attempts to make your legitimate users unknowingly submit data
that they don't intend to submit.
You can find more details about CSRF Protection and CSRF token in the `Symfony2 Book:
<http://symfony.com/doc/current/book/forms.html#csrf-protection>`
You can find more details about CSRF Protection and CSRF token in the
`Symfony2 Book
<http://symfony.com/doc/current/book/forms.html#csrf-protection>`_.
In some cases (for example, when embedding a form in an html email) you might want
not to use this protection. The easiest way to avoid this is to understand that it
is possible to give specific options to your form builder through the `createBuilder()` function.
In some cases (for example, when embedding a form in an html email) you might
want not to use this protection. The easiest way to avoid this is to
understand that it is possible to give specific options to your form builder
through the ``createBuilder()`` function.
Example
-------
::
.. code-block:: php
$form = $app['form.factory']->createBuilder('form', null, array('csrf_protection' => false));
That's it, your form could be submited from everywhere without CSRF Protection.
Going further
-------------
Going further..
---------------
This specific example showed how to change the `csrf_protection` in the `$options`
parameter of the `createBuilder()` function. More of them could be passed through
this parameter, it is as simple as using the Symfony2 `getDefaultOptions()` method
in your form classes. `See more here
<http://symfony.com/doc/current/book/forms.html#book-form-creating-form-classes>`
This specific example showed how to change the ``csrf_protection`` in the
``$options`` parameter of the ``createBuilder()`` function. More of them could
be passed through this parameter, it is as simple as using the Symfony2
``getDefaultOptions()`` method in your form classes. `See more here
<http://symfony.com/doc/current/book/forms.html#book-form-creating-form-classes>`_.
......@@ -22,8 +22,10 @@ Recipes
* :doc:`Translating Validation Messages<translating_validation_messages>`.
* :doc:`How to use PdoSessionStorage to store sessions in the database <session_storage>`.
* :doc:`How to use PdoSessionStorage to store sessions in the database
<session_storage>`.
* :doc:`How to disable the CSRF Protection on a form using the FormExtension <form_no_csrf>`.
* :doc:`How to disable the CSRF Protection on a form using the FormExtension
<form_no_csrf>`.
* :doc:`How to use YAML to configure validation <validator_yaml>`.
......@@ -16,9 +16,9 @@ Request
~~~~~~~
In the request we send the data for the blog post as a JSON object. We also
indicate that using the ``Content-Type`` header.
indicate that using the ``Content-Type`` header:
::
.. code-block:: text
POST /blog/posts
Accept: application/json
......@@ -32,9 +32,9 @@ Response
The server responds with a 201 status code, telling us that the post was
created. It tells us the ``Content-Type`` of the response, which is also
JSON.
JSON:
::
.. code-block:: text
HTTP/1.1 201 Created
Content-Type: application/json
......@@ -51,9 +51,7 @@ begins with ``application/json``. Since we want to do this for every request,
the easiest solution is to use a before filter.
We simply use ``json_decode`` to parse the content of the request and then
replace the request data on the ``$request`` object.
::
replace the request data on the ``$request`` object:
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ParameterBag;
......@@ -69,9 +67,7 @@ Controller implementation
-------------------------
Our controller will create a new blog post from the data provided and will
return the post object, including its ``id``, as JSON.
::
return the post object, including its ``id``, as JSON:
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
......@@ -91,9 +87,9 @@ Manual testing
--------------
In order to manually test our API, we can use the ``curl`` command line
utility, which allows sending HTTP requests.
utility, which allows sending HTTP requests:
::
.. code-block:: bash
$ curl http://blog.lo/blog/posts -d '{"title":"Hello World!","body":"This is my first post!"}' -H 'Content-Type: application/json'
{"id":"1","title":"Hello World!","body":"This is my first post!"}
......@@ -7,15 +7,14 @@ medium to large websites use a database to store sessions instead of files,
because databases are easier to use and scale in a multi-webserver
environment.
Symfony2's ``NativeSessionStorage`` has multiple storage handlers and one of them uses PDO to
store sessions, ``PdoSessionHandler``.
To use it, replace the ``session.storage.handler`` service in your application like
explained below.
Symfony2's ``NativeSessionStorage`` has multiple storage handlers and one of
them uses PDO to store sessions, ``PdoSessionHandler``. To use it, replace the
``session.storage.handler`` service in your application like explained below.
Example
-------
::
.. code-block:: php
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
......@@ -59,4 +58,4 @@ PdoSessionStorage needs a database table with 3 columns:
You can find examples of SQL statements to create the session table in the
`Symfony2 cookbook
<http://symfony.com/doc/current/cookbook/configuration/pdo_session_storage.html>`
<http://symfony.com/doc/current/cookbook/configuration/pdo_session_storage.html>`_
Translating Validation Messages
===============================
When working with Symfony2 validator, a common task would be to show localized validation messages.
When working with Symfony2 validator, a common task would be to show localized
validation messages.
In order to do that, you will need to register translator and point to translated resources:
::
In order to do that, you will need to register translator and point to
translated resources::
$app->register(new Silex\Provider\TranslationServiceProvider(), array(
'locale' => 'sr_Latn',
'translator.messages' => array()
'translator.domains' => array(),
));
$app->before(function () use ($app) {
$app['translator']->addLoader('xlf', new Symfony\Component\Translation\Loader\XliffFileLoader());
$app['translator']->addResource('xlf', __DIR__ . '/vendor/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.sr_Latn.xlf', 'sr_Latn', 'validators');
$app['translator']->addResource('xlf', __DIR__.'/vendor/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.sr_Latn.xlf', 'sr_Latn', 'validators');
});
And that's all you need to load translations from Symfony2 ``xlf`` files.
\ No newline at end of file
And that's all you need to load translations from Symfony2 ``xlf`` files.
......@@ -15,9 +15,7 @@ your ``composer.json`` file:
}
Next, you need to tell the Validation Service that you are not using
``StaticMethodLoader`` to load your class metadata but a YAML file:
.. code-block:: php
``StaticMethodLoader`` to load your class metadata but a YAML file::
$app->register(new ValidatorServiceProvider());
......
......@@ -10,68 +10,59 @@ Silex
Application
~~~~~~~~~~~
The application is the main interface to Silex. It
implements Symfony2's `HttpKernelInterface
The application is the main interface to Silex. It implements Symfony2's
`HttpKernelInterface
<http://api.symfony.com/master/Symfony/Component/HttpKernel/HttpKernelInterface.html>`_,
so you can pass a `Request
<http://api.symfony.com/master/Symfony/Component/HttpFoundation/Request.html>`_
to the ``handle`` method and it will return a `Response
<http://api.symfony.com/master/Symfony/Component/HttpFoundation/Response.html>`_.
It extends the ``Pimple`` service container, allowing
for flexibility on the outside as well as the inside. you
could replace any service, and you are also able to read
them.
It extends the ``Pimple`` service container, allowing for flexibility on the
outside as well as the inside. you could replace any service, and you are also
able to read them.
The application makes strong use of the `EventDispatcher
<http://api.symfony.com/master/Symfony/Component/EventDispatcher/EventDispatcher.html>`_
to hook into the Symfony2 `HttpKernel
<http://api.symfony.com/master/Symfony/Component/HttpKernel/HttpKernel.html>`_ events. This allows
fetching the ``Request``, converting string responses into
``Response`` objects and handling Exceptions. We also use it
to dispatch some custom events like before/after filters and
errors.
<http://api.symfony.com/master/Symfony/Component/HttpKernel/HttpKernel.html>`_
events. This allows fetching the ``Request``, converting string responses into
``Response`` objects and handling Exceptions. We also use it to dispatch some
custom events like before/after filters and errors.
Controller
~~~~~~~~~~
The Symfony2 `Route
<http://api.symfony.com/master/Symfony/Component/Routing/Route.html>`_
is actually quite powerful. Routes
can be named, which allows for URL generation. They can
also have requirements for the variable parts. In order
to allow settings these through a nice interface the
``match`` method (which is used by ``get``, ``post``, etc.)
returns an instance of the ``Controller``, which wraps
a route.
<http://api.symfony.com/master/Symfony/Component/Routing/Route.html>`_ is
actually quite powerful. Routes can be named, which allows for URL generation.
They can also have requirements for the variable parts. In order to allow
settings these through a nice interface the ``match`` method (which is used by
``get``, ``post``, etc.) returns an instance of the ``Controller``, which
wraps a route.
ControllerCollection
~~~~~~~~~~~~~~~~~~~~
One of the goals of exposing the `RouteCollection
<http://api.symfony.com/master/Symfony/Component/Routing/RouteCollection.html>`_
was to make it mutable, so providers could add stuff to it.
The challenge here is the fact that routes know nothing
about their name. The name only has meaning in context
of the ``RouteCollection`` and cannot be changed.
To solve this challenge we came up with a staging area
for routes. The ``ControllerCollection`` holds the
controllers until ``flush`` is called, at which point
the routes are added to the ``RouteCollection``. Also,
the controllers are then frozen. This means that they can
no longer be modified and will throw an Exception if
you try to do so.
Unfortunately no good way for flushing implicitly
could be found, which is why flushing is now always
explicit. The Application will flush, but if you want
to read the ``ControllerCollection`` before the
request takes place, you will have to call flush
yourself.
The ``Application`` provides a shortcut ``flush``
method for flushing the ``ControllerCollection``.
was to make it mutable, so providers could add stuff to it. The challenge here
is the fact that routes know nothing about their name. The name only has
meaning in context of the ``RouteCollection`` and cannot be changed.
To solve this challenge we came up with a staging area for routes. The
``ControllerCollection`` holds the controllers until ``flush`` is called, at
which point the routes are added to the ``RouteCollection``. Also, the
controllers are then frozen. This means that they can no longer be modified
and will throw an Exception if you try to do so.
Unfortunately no good way for flushing implicitly could be found, which is why
flushing is now always explicit. The Application will flush, but if you want
to read the ``ControllerCollection`` before the request takes place, you will
have to call flush yourself.
The ``Application`` provides a shortcut ``flush`` method for flushing the
``ControllerCollection``.
Symfony2
--------
......@@ -88,5 +79,4 @@ Following Symfony2 components are used by Silex:
* **EventDispatcher**: For hooking into the HttpKernel.
For more information, `check out the Symfony website
<http://symfony.com/>`_.
For more information, `check out the Symfony website <http://symfony.com/>`_.
Introduction
============
Silex is a PHP microframework for PHP 5.3. It is built on the shoulders
of Symfony2 and Pimple and also inspired by sinatra.
Silex is a PHP microframework for PHP 5.3. It is built on the shoulders of
Symfony2 and Pimple and also inspired by sinatra.
A microframework provides the guts for building simple single-file apps.
Silex aims to be:
A microframework provides the guts for building simple single-file apps. Silex
aims to be:
* *Concise*: Silex exposes an intuitive and concise API that is fun to use.
* *Extensible*: Silex has an extension system based around the Pimple
micro service-container that makes it even easier to tie in third party
libraries.
* *Extensible*: Silex has an extension system based around the Pimple micro
service-container that makes it even easier to tie in third party libraries.
* *Testable*: Silex uses Symfony2's HttpKernel which abstracts request and
response. This makes it very easy to test apps and the framework itself.
It also respects the HTTP specification and encourages its proper use.
response. This makes it very easy to test apps and the framework itself. It
also respects the HTTP specification and encourages its proper use.
In a nutshell, you define controllers and map them to routes, all in one
step.
In a nutshell, you define controllers and map them to routes, all in one step.
**Let's go!**::
......@@ -37,12 +35,12 @@ step.
All that is needed to get access to the Framework is to include the
autoloader.
Next we define a route to ``/hello/{name}`` that matches for ``GET``
requests. When the route matches, the function is executed and the return
value is sent back to the client.
Next we define a route to ``/hello/{name}`` that matches for ``GET`` requests.
When the route matches, the function is executed and the return value is sent
back to the client.
Finally, the app is run. Visit ``/hello/world`` to see the result.
It's really that easy!
Finally, the app is run. Visit ``/hello/world`` to see the result. It's really
that easy!
Installing Silex is as easy as it can get. Download the `silex.zip`_ file,
extract it, and you're done!
......
......@@ -31,26 +31,23 @@ will be set **before** the provider is registered::
Conventions
~~~~~~~~~~~
You need to watch out in what order you do certain things when
interacting with providers. Just keep to these rules:
You need to watch out in what order you do certain things when interacting
with providers. Just keep to these rules:
* Overriding existing services must occur **after** the
provider is registered.
* Overriding existing services must occur **after** the provider is
registered.
*Reason: If the services already exist, the provider
will overwrite it.*
*Reason: If the services already exist, the provider will overwrite it.*
* You can set parameters any time before the service is
accessed.
* You can set parameters any time before the service is accessed.
Make sure to stick to this behavior when creating your
own providers.
Make sure to stick to this behavior when creating your own providers.
Included providers
~~~~~~~~~~~~~~~~~~
There are a few provider that you get out of the box.
All of these are within the ``Silex\Provider`` namespace.
There are a few provider that you get out of the box. All of these are within
the ``Silex\Provider`` namespace:
* :doc:`DoctrineServiceProvider <providers/doctrine>`
* :doc:`MonologServiceProvider <providers/monolog>`
......@@ -66,8 +63,8 @@ All of these are within the ``Silex\Provider`` namespace.
Third party providers
~~~~~~~~~~~~~~~~~~~~~
Some service providers are developed by the community. Those
third-party providers are listed on `Silex' repository wiki
Some service providers are developed by the community. Those third-party
providers are listed on `Silex' repository wiki
<https://github.com/fabpot/Silex/wiki/Third-Party-ServiceProviders>`_.
You are encouraged to share yours.
......@@ -82,10 +79,9 @@ Providers must implement the ``Silex\ServiceProviderInterface``::
function register(Application $app);
}
This is very straight forward, just create a new class that
implements the ``register`` method. In this method you must
define services on the application which then may make use
of other services and parameters.
This is very straight forward, just create a new class that implements the
``register`` method. In this method you must define services on the
application which then may make use of other services and parameters.
Here is an example of such a provider::
......@@ -107,10 +103,9 @@ Here is an example of such a provider::
}
}
This class provides a ``hello`` service which is a protected
closure. It takes a ``name`` argument and will return
``hello.default_name`` if no name is given. If the default
is also missing, it will use an empty string.
This class provides a ``hello`` service which is a protected closure. It takes
a ``name`` argument and will return ``hello.default_name`` if no name is
given. If the default is also missing, it will use an empty string.
You can now use this provider as follows::
......@@ -126,8 +121,8 @@ You can now use this provider as follows::
return $app['hello']($name);
});
In this example we are getting the ``name`` parameter from the
query string, so the request path would have to be ``/hello?name=Fabien``.
In this example we are getting the ``name`` parameter from the query string,
so the request path would have to be ``/hello?name=Fabien``.
Controllers providers
---------------------
......
......@@ -8,7 +8,6 @@ Silex
monolog
session
swiftmailer
symfony_bridges
translation
twig
url_generator
......
MonologServiceProvider
======================
The *MonologServiceProvider* provides a default logging mechanism
through Jordi Boggiano's `Monolog <https://github.com/Seldaek/monolog>`_
library.
The *MonologServiceProvider* provides a default logging mechanism through
Jordi Boggiano's `Monolog <https://github.com/Seldaek/monolog>`_ library.
It will log requests and errors and allow you to add debug
logging to your application, so you don't have to use
``var_dump`` so much anymore. You can use the grown-up
version called ``tail -f``.
It will log requests and errors and allow you to add debug logging to your
application, so you don't have to use ``var_dump`` so much anymore. You can
use the grown-up version called ``tail -f``.
Parameters
----------
......@@ -33,9 +31,8 @@ Services
$app['monolog']->addDebug('Testing the Monolog logging.');
* **monolog.configure**: Protected closure that takes the
logger as an argument. You can override it if you do not
want the default behavior.
* **monolog.configure**: Protected closure that takes the logger as an
argument. You can override it if you do not want the default behavior.
Registering
-----------
......@@ -60,11 +57,9 @@ Registering
Usage
-----
The MonologServiceProvider provides a ``monolog`` service. You can use
it to add log entries for any logging level through ``addDebug()``,
``addInfo()``, ``addWarning()`` and ``addError()``.
::
The MonologServiceProvider provides a ``monolog`` service. You can use it to
add log entries for any logging level through ``addDebug()``, ``addInfo()``,
``addWarning()`` and ``addError()``:
use Symfony\Component\HttpFoundation\Response;
......
......@@ -24,8 +24,8 @@ Parameters
* **secure**: Cookie secure (HTTPS)
* **httponly**: Whether the cookie is http only
However, all of these are optional. Sessions last as long as the browser
is open. To override this, set the ``lifetime`` option.
However, all of these are optional. Sessions last as long as the browser is
open. To override this, set the ``lifetime`` option.
Services
--------
......@@ -33,11 +33,12 @@ Services
* **session**: An instance of Symfony2's `Session
<http://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Session.html>`_.
* **session.storage**: A service that is used for persistence of the
session data. Defaults to a ``NativeSessionStorage``
* **session.storage.handler**: A service that is used by the ``session.storage``
for data access. Defaults to a ``NativeFileSessionHandler`` storage handler.
* **session.storage**: A service that is used for persistence of the session
data. Defaults to a ``NativeSessionStorage``.
* **session.storage.handler**: A service that is used by the
``session.storage`` for data access. Defaults to a
``NativeFileSessionHandler`` storage handler.
Registering
-----------
......@@ -49,8 +50,8 @@ Registering
Usage
-----
The Session provider provides a ``session`` service. Here is an
example that authenticates a user and creates a session for him::
The Session provider provides a ``session`` service. Here is an example that
authenticates a user and creates a session for him::
use Symfony\Component\HttpFoundation\Response;
......
SwiftmailerServiceProvider
==========================
The *SwiftmailerServiceProvider* provides a service for sending
email through the `Swift Mailer <http://swiftmailer.org>`_
library.
The *SwiftmailerServiceProvider* provides a service for sending email through
the `Swift Mailer <http://swiftmailer.org>`_ library.
You can use the ``mailer`` service to send messages easily.
By default, it will attempt to send emails through SMTP.
You can use the ``mailer`` service to send messages easily. By default, it
will attempt to send emails through SMTP.
Parameters
----------
* **swiftmailer.options**: An array of options for the default
SMTP-based configuration.
* **swiftmailer.options**: An array of options for the default SMTP-based
configuration.
The following options can be set:
......@@ -75,9 +74,7 @@ Registering
Usage
-----
The Swiftmailer provider provides a ``mailer`` service.
::
The Swiftmailer provider provides a ``mailer`` service:
$app->post('/feedback', function () use ($app) {
$request = $app['request'];
......
SymfonyBridgesServiceProvider
=============================
The *SymfonyBridgesServiceProvider* provides additional integration between
Symfony2 components and libraries.
Parameters
----------
none
Twig
----
When the ``SymfonyBridgesServiceProvider`` is enabled, the ``TwigServiceProvider`` will
provide you with the following additional capabilities:
* **UrlGeneratorServiceProvider**: If you are using the ``UrlGeneratorServiceProvider``,
you will get ``path`` and ``url`` helpers for Twig. You can find more
information in the
`Symfony2 Routing documentation <http://symfony.com/doc/current/book/routing.html#generating-urls-from-a-template>`_.
* **TranslationServiceProvider**: If you are using the ``TranslationServiceProvider``,
you will get ``trans`` and ``transchoice`` helpers for translation in
Twig templates. You can find more information in the
`Symfony2 Translation documentation <http://symfony.com/doc/current/book/translation.html#twig-templates>`_.
* **FormServiceProvider**: If you are using the ``FormServiceProvider``,
you will get a set of helpers for working with forms in templates.
You can find more information in the
`Symfony2 Forms reference <http://symfony.com/doc/current/reference/forms/twig_reference.html>`_.
Registering
-----------
.. code-block:: php
$app->register(new Silex\Provider\SymfonyBridgesServiceProvider());
.. note::
The Symfony bridges do not come with the ``silex.zip`, so you need to add
them as a dependency to your ``composer.json`` file:
.. code-block:: json
"require": {
"symfony/twig-bridge": "2.1.*",
}
......@@ -7,10 +7,8 @@ application into different languages.
Parameters
----------
* **translator.messages** (optional): A mapping of locales to message arrays.
This parameter contains the translation data in all languages.
* **translator.domains** (optional): Same as above but stored by domains.
* **translator.domains**: A mapping of domains/locales/messages. This
parameter contains the translation data for all languages and domains.
* **locale** (optional): The locale for the translator. You will most likely
want to set this based on some request parameter. Defaults to ``en``.
......@@ -26,7 +24,8 @@ Services
that is used for translation.
* **translator.loader**: An instance of an implementation of the translation
`LoaderInterface <http://api.symfony.com/master/Symfony/Component/Translation/Loader/LoaderInterface.html>`_,
`LoaderInterface
<http://api.symfony.com/master/Symfony/Component/Translation/Loader/LoaderInterface.html>`_,
defaults to an `ArrayLoader
<http://api.symfony.com/master/Symfony/Component/Translation/Loader/ArrayLoader.html>`_.
......@@ -58,20 +57,27 @@ Usage
-----
The Translation provider provides a ``translator`` service and makes use of
the ``translator.messages`` parameter::
the ``translator.domains`` parameter::
$app['translator.messages'] = array(
'en' => array(
'hello' => 'Hello %name%',
'goodbye' => 'Goodbye %name%',
),
'de' => array(
'hello' => 'Hallo %name%',
'goodbye' => 'Tschüss %name%',
$app['translator.domains'] = array(
'messages' => array(
'en' => array(
'hello' => 'Hello %name%',
'goodbye' => 'Goodbye %name%',
),
'de' => array(
'hello' => 'Hallo %name%',
'goodbye' => 'Tschüss %name%',
),
'fr' => array(
'hello' => 'Bonjour %name%',
'goodbye' => 'Au revoir %name%',
),
),
'fr' => array(
'hello' => 'Bonjour %name%',
'goodbye' => 'Au revoir %name%',
'validators' => array(
'fr' => array(
'This value should be a valid number.' => 'Cette valeur doit être un nombre.',
),
),
);
......@@ -95,33 +101,6 @@ The above example will result in following routes:
* ``/it/hello/igor`` will return ``Hello igor`` (because of the fallback).
The messages defined with ``translator.messages`` are automatically stored in
the default domain. When you need to explicitly set the translation domain
(for the validation error messages for instance), use the
``translator.domains`` parameter instead::
$app['translator.domains'] = array(
'messages' => array(
'en' => array(
'hello' => 'Hello %name%',
'goodbye' => 'Goodbye %name%',
),
'de' => array(
'hello' => 'Hallo %name%',
'goodbye' => 'Tschüss %name%',
),
'fr' => array(
'hello' => 'Bonjour %name%',
'goodbye' => 'Au revoir %name%',
),
),
'validators' => array(
'fr' => array(
'This value should be a valid number.' => 'Cette valeur doit être un nombre.',
),
),
);
Recipes
-------
......@@ -149,13 +128,15 @@ use is ``locales/en.yml``. Just do the mapping in this file as follows:
hello: Hello %name%
goodbye: Goodbye %name%
Repeat this for all of your languages. Then set up the ``translator.messages``
Repeat this for all of your languages. Then set up the ``translator.domains``
to map languages to files::
$app['translator.messages'] = array(
'en' => __DIR__.'/locales/en.yml',
'de' => __DIR__.'/locales/de.yml',
'fr' => __DIR__.'/locales/fr.yml',
$app['translator.domains'] = array(
'messages' => array(
'en' => __DIR__.'/locales/en.yml',
'de' => __DIR__.'/locales/de.yml',
'fr' => __DIR__.'/locales/fr.yml',
),
);
Finally override the ``translator.loader`` to use a ``YamlFileLoader`` instead
......@@ -176,7 +157,7 @@ Just as you would do with YAML translation files, you first need to add the
Symfony2 ``Config`` component as a dependency (see above for details).
Then, similarly, create XLIFF files in your locales directory and setup the
``translator.messages`` to map to them.
``translator.domains`` to map to them.
Finally override the ``translator.loader`` to use a ``XliffFileLoader``::
......@@ -191,7 +172,8 @@ That's it.
Accessing translations in Twig templates
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once loaded, the translation service provider is available from within Twig templates:
Once loaded, the translation service provider is available from within Twig
templates:
.. code-block:: jinja
......
......@@ -29,9 +29,9 @@ Services
that takes the Twig environment as an argument. You can use it to add more
custom globals.
* **twig.loader**: The loader for Twig templates which uses
the ``twig.path`` and the ``twig.templates`` options. You
can also replace the loader completely.
* **twig.loader**: The loader for Twig templates which uses the ``twig.path``
and the ``twig.templates`` options. You can also replace the loader
completely.
Registering
-----------
......@@ -53,6 +53,39 @@ Registering
"twig/twig": ">=1.8,<2.0-dev"
}
Symfony2 Components Integration
-------------------------------
Symfony provides a Twig bridge that provides additional integration between
some Symfony2 components and Twig. Add it as a dependency to your
``composer.json`` file:
.. code-block:: json
"require": {
"symfony/twig-bridge": "2.1.*",
}
When present, the ``TwigServiceProvider`` will provide you with the following
additional capabilities:
* **UrlGeneratorServiceProvider**: If you are using the
``UrlGeneratorServiceProvider``, you will have access to the ``path()`` and
``url()`` functions. You can find more information in the `Symfony2 Routing
documentation
<http://symfony.com/doc/current/book/routing.html#generating-urls-from-a-template>`_.
* **TranslationServiceProvider**: If you are using the
``TranslationServiceProvider``, you will get the ``trans()`` and
``transchoice()`` functions for translation in Twig templates. You can find
more information in the `Symfony2 Translation documentation
<http://symfony.com/doc/current/book/translation.html#twig-templates>`_.
* **FormServiceProvider**: If you are using the ``FormServiceProvider``, you
will get a set of helpers for working with forms in templates. You can find
more information in the `Symfony2 Forms reference
<http://symfony.com/doc/current/reference/forms/twig_reference.html>`_.
Usage
-----
......
UrlGeneratorServiceProvider
===========================
The *UrlGeneratorServiceProvider* provides a service for generating
URLs for named routes.
The *UrlGeneratorServiceProvider* provides a service for generating URLs for
named routes.
Parameters
----------
......@@ -16,9 +16,9 @@ Services
<http://api.symfony.com/master/Symfony/Component/Routing/Generator/UrlGenerator.html>`_,
using the `RouteCollection
<http://api.symfony.com/master/Symfony/Component/Routing/RouteCollection.html>`_
that is provided through the ``routes`` service.
It has a ``generate`` method, which takes the route name as an argument,
followed by an array of route parameters.
that is provided through the ``routes`` service. It has a ``generate``
method, which takes the route name as an argument, followed by an array of
route parameters.
Registering
-----------
......
This diff is collapsed.
......@@ -3,30 +3,32 @@ Testing
Because Silex is built on top of Symfony2, it is very easy to write functional
tests for your application. Functional tests are automated software tests that
ensure that your code is working correctly. They go through the user interface,
using a fake browser, and mimic the actions a user would do.
ensure that your code is working correctly. They go through the user
interface, using a fake browser, and mimic the actions a user would do.
Why
---
If you are not familiar with software tests, you may be wondering why you would
need this. Every time you make a change to your application, you have to test
it. This means going through all the pages and making sure they are still
If you are not familiar with software tests, you may be wondering why you
would need this. Every time you make a change to your application, you have to
test it. This means going through all the pages and making sure they are still
working. Functional tests save you a lot of time, because they enable you to
test your application in usually under a second by running a single command.
For more information on functional testing, unit testing, and automated
software tests in general, check out `PHPUnit <https://github.com/sebastianbergmann/phpunit>`_
and `Bulat Shakirzyanov's talk on Clean Code <http://www.slideshare.net/avalanche123/clean-code-5609451>`_.
software tests in general, check out `PHPUnit
<https://github.com/sebastianbergmann/phpunit>`_ and `Bulat Shakirzyanov's
talk on Clean Code
<http://www.slideshare.net/avalanche123/clean-code-5609451>`_.
PHPUnit
-------
`PHPUnit <https://github.com/sebastianbergmann/phpunit>`_
is the de-facto standard testing framework for PHP. It was built for
writing unit tests, but it can be used for functional tests too. You write
tests by creating a new class, that extends the ``PHPUnit_Framework_TestCase``.
Your test cases are methods prefixed with ``test``::
`PHPUnit <https://github.com/sebastianbergmann/phpunit>`_ is the de-facto
standard testing framework for PHP. It was built for writing unit tests, but
it can be used for functional tests too. You write tests by creating a new
class, that extends the ``PHPUnit_Framework_TestCase``. Your test cases are
methods prefixed with ``test``::
class ContactFormTest extends PHPUnit_Framework_TestCase
{
......@@ -120,8 +122,9 @@ executed before every test.
// ...
}
The WebTestCase provides a ``createClient`` method. A client acts as a browser,
and allows you to interact with your application. Here's how it works::
The WebTestCase provides a ``createClient`` method. A client acts as a
browser, and allows you to interact with your application. Here's how it
works::
public function testInitialPage()
{
......@@ -148,8 +151,8 @@ application.
.. note::
You can find some documentation for it in `the client section of the testing
chapter of the Symfony2 documentation
You can find some documentation for it in `the client section of the
testing chapter of the Symfony2 documentation
<http://symfony.com/doc/current/book/testing.html#the-test-client>`_.
Crawler
......@@ -168,8 +171,9 @@ Configuration
-------------
The suggested way to configure PHPUnit is to create a ``phpunit.xml.dist``
file, a ``tests`` folder and your tests in ``tests/YourApp/Tests/YourTest.php``.
The ``phpunit.xml.dist`` file should look like this:
file, a ``tests`` folder and your tests in
``tests/YourApp/Tests/YourTest.php``. The ``phpunit.xml.dist`` file should
look like this:
.. code-block:: xml
......
This diff is collapsed.
......@@ -48,6 +48,9 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
{
const VERSION = '@package_version@';
private $providers = array();
private $booted = false;
/**
* Constructor.
*/
......@@ -55,6 +58,8 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
{
$app = $this;
$this['logger'] = null;
$this['routes'] = $this->share(function () {
return new RouteCollection();
});
......@@ -74,13 +79,13 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
$urlMatcher = new LazyUrlMatcher(function () use ($app) {
return $app['url_matcher'];
});
$dispatcher->addSubscriber(new RouterListener($urlMatcher));
$dispatcher->addSubscriber(new RouterListener($urlMatcher, $app['logger']));
return $dispatcher;
});
$this['resolver'] = $this->share(function () use ($app) {
return new ControllerResolver($app);
return new ControllerResolver($app, $app['logger']);
});
$this['kernel'] = $this->share(function () use ($app) {
......@@ -145,9 +150,22 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
$this[$key] = $value;
}
$this->providers[] = $provider;
$provider->register($this);
}
public function boot()
{
if (!$this->booted) {
foreach ($this->providers as $provider) {
$provider->boot($this);
}
$this->booted = true;
}
}
/**
* Maps a pattern to a callable.
*
......@@ -437,6 +455,10 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
*/
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
if (!$this->booted) {
$this->boot();
}
$this->beforeDispatched = false;
$current = HttpKernelInterface::SUB_REQUEST === $type ? $this['request'] : $this['request_error'];
......
......@@ -119,4 +119,8 @@ class DoctrineServiceProvider implements ServiceProviderInterface
return $dbs[$app['dbs.default']];
});
}
public function boot(Application $app)
{
}
}
......@@ -52,4 +52,8 @@ class FormServiceProvider implements ServiceProviderInterface
return new DefaultCsrfProvider($app['form.secret']);
});
}
public function boot(Application $app)
{
}
}
......@@ -42,4 +42,8 @@ class HttpCacheServiceProvider implements ServiceProviderInterface
$app['http_cache.options'] = array();
}
}
public function boot(Application $app)
{
}
}
......@@ -13,10 +13,8 @@ namespace Silex\Provider;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Silex\Application;
use Silex\ServiceProviderInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
......@@ -29,8 +27,16 @@ class MonologServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['monolog'] = $app->share(function () use ($app) {
$log = new Logger(isset($app['monolog.name']) ? $app['monolog.name'] : 'myapp');
if ($bridge = class_exists('Symfony\Bridge\Monolog\Logger')) {
$app['logger'] = function () use ($app) {
return $app['monolog'];
};
}
$app['monolog'] = $app->share(function () use ($app, $bridge) {
$class = $bridge ? 'Symfony\Bridge\Monolog\Logger' : 'Monolog\Logger';
$log = new $class(isset($app['monolog.name']) ? $app['monolog.name'] : 'myapp');
$app['monolog.configure']($log);
......@@ -50,14 +56,17 @@ class MonologServiceProvider implements ServiceProviderInterface
return Logger::DEBUG;
};
}
}
public function boot(Application $app)
{
$app->before(function (Request $request) use ($app) {
$app['monolog']->addInfo('> '.$request->getMethod().' '.$request->getRequestUri());
});
$app->error(function (\Exception $e) use ($app) {
$app['monolog']->addError($e->getMessage());
});
}, 255);
$app->after(function (Request $request, Response $response) use ($app) {
$app['monolog']->addInfo('< '.$response->getStatusCode());
......
......@@ -49,8 +49,6 @@ class SessionServiceProvider implements ServiceProviderInterface
);
});
$app['dispatcher']->addListener(KernelEvents::REQUEST, array($this, 'onKernelRequest'), 128);
if (!isset($app['session.storage.options'])) {
$app['session.storage.options'] = array();
}
......@@ -70,4 +68,9 @@ class SessionServiceProvider implements ServiceProviderInterface
$request->getSession()->start();
}
}
public function boot(Application $app)
{
$app['dispatcher']->addListener(KernelEvents::REQUEST, array($this, 'onKernelRequest'), 128);
}
}
......@@ -76,14 +76,17 @@ class SwiftmailerServiceProvider implements ServiceProviderInterface
return new \Swift_Events_SimpleEventDispatcher();
});
$app->finish(function () use ($app) {
$app['swiftmailer.spooltransport']->getSpool()->flushQueue($app['swiftmailer.transport']);
});
if (isset($app['swiftmailer.class_path'])) {
require_once $app['swiftmailer.class_path'].'/Swift.php';
\Swift::registerAutoload($app['swiftmailer.class_path'].'/../swift_init.php');
}
}
public function boot(Application $app)
{
$app->finish(function () use ($app) {
$app['swiftmailer.spooltransport']->getSpool()->flushQueue($app['swiftmailer.transport']);
});
}
}
<?php
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Silex\Provider;
use Silex\Application;
use Silex\ServiceProviderInterface;
/**
* Symfony bridges Provider.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SymfonyBridgesServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['symfony_bridges'] = true;
}
}
......@@ -36,17 +36,9 @@ class TranslationServiceProvider implements ServiceProviderInterface
$translator->addLoader('array', $app['translator.loader']);
if (isset($app['translator.messages'])) {
foreach ($app['translator.messages'] as $locale => $messages) {
$translator->addResource('array', $messages, $locale);
}
}
if (isset($app['translator.domains'])) {
foreach ($app['translator.domains'] as $domain => $data) {
foreach ($data as $locale => $messages) {
$translator->addResource('array', $messages, $locale, $domain);
}
foreach ($app['translator.domains'] as $domain => $data) {
foreach ($data as $locale => $messages) {
$translator->addResource('array', $messages, $locale, $domain);
}
}
......@@ -61,4 +53,8 @@ class TranslationServiceProvider implements ServiceProviderInterface
return new MessageSelector();
});
}
public function boot(Application $app)
{
}
}
......@@ -45,7 +45,7 @@ class TwigServiceProvider implements ServiceProviderInterface
$twig->addExtension(new \Twig_Extension_Debug());
}
if (isset($app['symfony_bridges'])) {
if (class_exists('Symfony\Bridge\Twig\Extension\RoutingExtension')) {
if (isset($app['url_generator'])) {
$twig->addExtension(new TwigRoutingExtension($app['url_generator']));
}
......@@ -90,4 +90,8 @@ class TwigServiceProvider implements ServiceProviderInterface
));
});
}
public function boot(Application $app)
{
}
}
......@@ -31,4 +31,8 @@ class UrlGeneratorServiceProvider implements ServiceProviderInterface
return new UrlGenerator($app['routes'], $app['request_context']);
});
}
public function boot(Application $app)
{
}
}
......@@ -43,4 +43,8 @@ class ValidatorServiceProvider implements ServiceProviderInterface
return new ConstraintValidatorFactory();
});
}
public function boot(Application $app)
{
}
}
......@@ -21,7 +21,19 @@ interface ServiceProviderInterface
/**
* Registers services on the given app.
*
* This method should only be used to configure services and parameters.
* It should not get services.
*
* @param Application $app An Application instance
*/
function register(Application $app);
/**
* Bootstraps the application.
*
* This method is called after all services are registers
* and should be used for "dynamic" configuration (whenever
* a service must be requested).
*/
function boot(Application $app);
}
......@@ -24,7 +24,7 @@ class DoctrineServiceProviderTest extends \PHPUnit_Framework_TestCase
public function setUp()
{
if (!is_dir(__DIR__.'/../../../../vendor/doctrine/common/lib') || !is_dir(__DIR__.'/../../../../vendor/doctrine/dbal/lib')) {
$this->markTestSkipped('Doctrine Common/DBAL submodules were not installed.');
$this->markTestSkipped('Doctrine Common/DBAL dependencies were not installed.');
}
}
......
......@@ -12,10 +12,8 @@
namespace Silex\Tests\Provider;
use Monolog\Handler\TestHandler;
use Silex\Application;
use Silex\Provider\MonologServiceProvider;
use Symfony\Component\HttpFoundation\Request;
/**
......@@ -28,28 +26,14 @@ class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase
public function setUp()
{
if (!is_dir(__DIR__.'/../../../../vendor/monolog/monolog/src')) {
$this->markTestSkipped('Monolog submodule was not installed.');
$this->markTestSkipped('Monolog dependency was not installed.');
}
}
public function testRegister()
public function testRequestLogging()
{
$app = new Application();
$app->register(new MonologServiceProvider());
$app['monolog.handler'] = $app->share(function () use ($app) {
return new TestHandler($app['monolog.level']);
});
$app = $this->getApplication();
return $app;
}
/**
* @depends testRegister
*/
public function testRequestLogging($app)
{
$app->get('/foo', function () use ($app) {
return 'foo';
});
......@@ -59,14 +43,15 @@ class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase
$request = Request::create('/foo');
$app->handle($request);
$this->assertTrue($app['monolog.handler']->hasInfoRecords());
$this->assertTrue($app['monolog.handler']->hasInfo('> GET /foo'));
$this->assertTrue($app['monolog.handler']->hasInfo('< 200'));
$this->assertTrue($app['monolog.handler']->hasInfo('Matched route "GET_foo" (parameters: "_controller": "{}", "_route": "GET_foo")'));
}
/**
* @depends testRegister
*/
public function testManualLogging($app)
public function testManualLogging()
{
$app = $this->getApplication();
$app->get('/log', function () use ($app) {
$app['monolog']->addDebug('logging a message');
});
......@@ -76,14 +61,13 @@ class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase
$request = Request::create('/log');
$app->handle($request);
$this->assertTrue($app['monolog.handler']->hasDebugRecords());
$this->assertTrue($app['monolog.handler']->hasDebug('logging a message'));
}
/**
* @depends testRegister
*/
public function testErrorLogging($app)
public function testErrorLogging()
{
$app = $this->getApplication();
$app->get('/error', function () {
throw new \RuntimeException('very bad error');
});
......@@ -97,6 +81,19 @@ class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase
$request = Request::create('/error');
$app->handle($request);
$this->assertTrue($app['monolog.handler']->hasErrorRecords());
$this->assertTrue($app['monolog.handler']->hasError('very bad error'));
}
protected function getApplication()
{
$app = new Application();
$app->register(new MonologServiceProvider());
$app['monolog.handler'] = $app->share(function () use ($app) {
return new TestHandler($app['monolog.level']);
});
return $app;
}
}
......@@ -26,7 +26,7 @@ class TwigServiceProviderTest extends \PHPUnit_Framework_TestCase
public function setUp()
{
if (!is_dir(__DIR__.'/../../../../vendor/twig/twig/lib')) {
$this->markTestSkipped('Twig submodule was not installed.');
$this->markTestSkipped('Twig dependency was not installed.');
}
}
......
......@@ -24,7 +24,7 @@ class ValidatorServiceProviderTest extends \PHPUnit_Framework_TestCase
public function setUp()
{
if (!is_dir(__DIR__.'/../../../../vendor/symfony/validator')) {
$this->markTestSkipped('Validator submodule was not installed.');
$this->markTestSkipped('Validator dependency was not installed.');
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment