├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── bin ├── build ├── compile └── skeleton │ ├── fat_composer.json │ ├── index.php │ └── slim_composer.json ├── composer.json ├── doc ├── changelog.rst ├── conf.py ├── contributing.rst ├── cookbook │ ├── error_handler.rst │ ├── form_no_csrf.rst │ ├── index.rst │ ├── json_request_body.rst │ ├── session_storage.rst │ ├── sub_requests.rst │ ├── translating_validation_messages.rst │ └── validator_yaml.rst ├── index.rst ├── internals.rst ├── intro.rst ├── middlewares.rst ├── organizing_controllers.rst ├── phar.rst ├── providers.rst ├── providers │ ├── doctrine.rst │ ├── form.rst │ ├── http_cache.rst │ ├── index.rst │ ├── monolog.rst │ ├── security.rst │ ├── serializer.rst │ ├── service_controller.rst │ ├── session.rst │ ├── swiftmailer.rst │ ├── translation.rst │ ├── twig.rst │ ├── url_generator.rst │ └── validator.rst ├── services.rst ├── testing.rst ├── usage.rst └── web_servers.rst ├── phpunit.xml.dist ├── src └── Silex │ ├── Application.php │ ├── Application │ ├── FormTrait.php │ ├── MonologTrait.php │ ├── SecurityTrait.php │ ├── SwiftmailerTrait.php │ ├── TranslationTrait.php │ ├── TwigTrait.php │ └── UrlGeneratorTrait.php │ ├── Controller.php │ ├── ControllerCollection.php │ ├── ControllerProviderInterface.php │ ├── ControllerResolver.php │ ├── EventListener │ ├── ConverterListener.php │ ├── LocaleListener.php │ ├── MiddlewareListener.php │ └── StringToResponseListener.php │ ├── Exception │ └── ControllerFrozenException.php │ ├── ExceptionHandler.php │ ├── ExceptionListenerWrapper.php │ ├── HttpCache.php │ ├── LazyUrlMatcher.php │ ├── Provider │ ├── DoctrineServiceProvider.php │ ├── FormServiceProvider.php │ ├── HttpCacheServiceProvider.php │ ├── MonologServiceProvider.php │ ├── SecurityServiceProvider.php │ ├── SerializerServiceProvider.php │ ├── ServiceControllerServiceProvider.php │ ├── SessionServiceProvider.php │ ├── SwiftmailerServiceProvider.php │ ├── TranslationServiceProvider.php │ ├── TwigCoreExtension.php │ ├── TwigServiceProvider.php │ ├── UrlGeneratorServiceProvider.php │ └── ValidatorServiceProvider.php │ ├── RedirectableUrlMatcher.php │ ├── Route.php │ ├── Route │ └── SecurityTrait.php │ ├── ServiceControllerResolver.php │ ├── ServiceProviderInterface.php │ ├── Util │ └── Compiler.php │ └── WebTestCase.php └── tests ├── Silex └── Tests │ ├── Application │ ├── FormApplication.php │ ├── FormTraitTest.php │ ├── MonologApplication.php │ ├── MonologTraitTest.php │ ├── SecurityApplication.php │ ├── SecurityTraitTest.php │ ├── SwiftmailerApplication.php │ ├── SwiftmailerTraitTest.php │ ├── TranslationApplication.php │ ├── TranslationTraitTest.php │ ├── TwigApplication.php │ ├── TwigTraitTest.php │ ├── UrlGeneratorApplication.php │ └── UrlGeneratorTraitTest.php │ ├── ApplicationTest.php │ ├── ControllerCollectionTest.php │ ├── ControllerResolverTest.php │ ├── ControllerTest.php │ ├── ExceptionHandlerTest.php │ ├── FunctionalTest.php │ ├── JsonTest.php │ ├── LazyUrlMatcherTest.php │ ├── LocaleTest.php │ ├── MiddlewareTest.php │ ├── Provider │ ├── DoctrineServiceProviderTest.php │ ├── HttpCacheServiceProviderTest.php │ ├── MonologServiceProviderTest.php │ ├── SecurityServiceProviderTest.php │ ├── SerializerServiceProviderTest.php │ ├── SessionServiceProviderTest.php │ ├── SpoolStub.php │ ├── SwiftmailerServiceProviderTest.php │ ├── TwigServiceProviderTest.php │ ├── UrlGeneratorServiceProviderTest.php │ └── ValidatorServiceProviderTest.php │ ├── Route │ ├── SecurityRoute.php │ └── SecurityTraitTest.php │ ├── RouterTest.php │ ├── ServiceControllerResolverRouterTest.php │ ├── ServiceControllerResolverTest.php │ ├── StreamTest.php │ └── WebTestCaseTest.php └── bootstrap.php /.gitignore: -------------------------------------------------------------------------------- 1 | /phpunit.xml 2 | /vendor 3 | /build 4 | /composer.lock 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | env: 4 | - SYMFONY_DEPS_VERSION=2.1 5 | - SYMFONY_DEPS_VERSION=2.2 6 | 7 | before_script: 8 | # symfony/* 9 | - sh -c "if [ '$SYMFONY_DEPS_VERSION' = '2.2' ]; then sed -i 's/>=2.1,<2.3-dev/2.2.*@dev/g' composer.json; fi" 10 | - composer install --dev --prefer-source 11 | 12 | php: 13 | - 5.3 14 | - 5.4 15 | - 5.5 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2013 Fabien Potencier 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Silex, a simple Web Framework 2 | ============================= 3 | 4 | [![Build Status](https://secure.travis-ci.org/fabpot/Silex.png?branch=master)](http://travis-ci.org/fabpot/Silex) 5 | 6 | Silex is a simple web framework to develop simple websites based on 7 | [Symfony2][1] components: 8 | 9 | 10 | ```php 11 | get('/hello/{name}', function ($name) use ($app) { 17 | return 'Hello '.$app->escape($name); 18 | }); 19 | 20 | $app->run(); 21 | ``` 22 | 23 | Silex works with PHP 5.3.3 or later. 24 | 25 | ## Installation 26 | 27 | The recommended way to install Silex is [through 28 | composer](http://getcomposer.org). Just create a `composer.json` file and 29 | run the `php composer.phar install` command to install it: 30 | 31 | { 32 | "require": { 33 | "silex/silex": "1.0.*@dev" 34 | } 35 | } 36 | 37 | Alternatively, you can download the [`silex.zip`][2] file and extract it. 38 | 39 | ## More Information 40 | 41 | Read the [documentation][3] for more information. 42 | 43 | ## Tests 44 | 45 | To run the test suite, you need [composer](http://getcomposer.org) and 46 | [PHPUnit](https://github.com/sebastianbergmann/phpunit). 47 | 48 | $ php composer.phar install --dev 49 | $ phpunit 50 | 51 | ## Community 52 | 53 | Check out #silex-php on irc.freenode.net. 54 | 55 | ## License 56 | 57 | Silex is licensed under the MIT license. 58 | 59 | [1]: http://symfony.com 60 | [2]: http://silex.sensiolabs.org/download 61 | [3]: http://silex.sensiolabs.org/documentation 62 | -------------------------------------------------------------------------------- /bin/build: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PHP=`which php` 4 | GIT=`which git` 5 | DIR=`$PHP -r "echo dirname(dirname(realpath('$0')));"` 6 | 7 | if [ ! -d "$DIR/build" ]; then 8 | mkdir -p $DIR/build 9 | fi 10 | 11 | cd $DIR/build 12 | 13 | if [ ! -f "composer.phar" ]; then 14 | curl -s http://getcomposer.org/installer 2>/dev/null | $PHP >/dev/null 2>/dev/null 15 | else 16 | $PHP composer.phar self-update >/dev/null 2>/dev/null 17 | fi 18 | 19 | for TYPE in slim fat 20 | do 21 | if [ -d "$DIR/build/skeleton" ]; then 22 | rm -rf $DIR/build/skeleton 23 | fi 24 | mkdir -p $DIR/build/skeleton 25 | 26 | cd "$DIR/build/skeleton" 27 | 28 | mkdir -p web/ 29 | COMPOSER=$TYPE"_composer.json" 30 | cp $DIR/bin/skeleton/$COMPOSER composer.json 31 | cp $DIR/bin/skeleton/index.php web/index.php 32 | 33 | $PHP ../composer.phar install -q 34 | 35 | if [ -d "$DIR/build/tmp/silex" ]; then 36 | rm -rf $DIR/build/tmp/silex 37 | fi 38 | mkdir -p $DIR/build/tmp/silex 39 | 40 | cd "$DIR/build/tmp/silex" 41 | cp -r ../../skeleton/* . 42 | 43 | find . -name .DS_Store | xargs rm -rf - 44 | find . -name .git | xargs rm -rf - 45 | find . -name phpunit.xml.* | xargs rm -rf - 46 | find . -type d -name Tests | xargs rm -rf - 47 | find . -type d -name test* | xargs rm -rf - 48 | find . -type d -name doc | xargs rm -rf - 49 | find . -type d -name ext | xargs rm -rf - 50 | 51 | export COPY_EXTENDED_ATTRIBUTES_DISABLE=true 52 | export COPYFILE_DISABLE=true 53 | 54 | cd "$DIR/build/tmp" 55 | 56 | if [ "slim" = "$TYPE" ]; then 57 | NAME="silex" 58 | else 59 | NAME="silex_fat" 60 | fi 61 | 62 | rm -f "$DIR/build/$NAME.*" 63 | tar zcpf "$DIR/build/$NAME.tgz" silex 64 | zip -rq "$DIR/build/$NAME.zip" silex 65 | rm -rf "$DIR/build/tmp" 66 | rm -rf "$DIR/build/skeleton" 67 | done 68 | -------------------------------------------------------------------------------- /bin/compile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | compile(); 10 | -------------------------------------------------------------------------------- /bin/skeleton/fat_composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "silex/silex": "1.0.*@dev", 4 | "symfony/browser-kit": "2.1.*", 5 | "symfony/console": "2.1.*", 6 | "symfony/config": "2.1.*", 7 | "symfony/css-selector": "2.1.*", 8 | "symfony/dom-crawler": "2.1.*", 9 | "symfony/filesystem": "2.1.*", 10 | "symfony/finder": "2.1.*", 11 | "symfony/form": "2.1.*", 12 | "symfony/locale": "2.1.*", 13 | "symfony/process": "2.1.*", 14 | "symfony/security": "2.1.*", 15 | "symfony/serializer": "2.1.*", 16 | "symfony/translation": "2.1.*", 17 | "symfony/validator": "2.1.*", 18 | "symfony/monolog-bridge": "2.1.*", 19 | "symfony/twig-bridge": "2.1.*", 20 | "monolog/monolog": ">=1.0.0,<1.2-dev", 21 | "twig/twig": ">=1.2.0,<2.0-dev", 22 | "doctrine/dbal": ">=2.2.0,<2.4.0-dev", 23 | "swiftmailer/swiftmailer": "4.1.*" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /bin/skeleton/index.php: -------------------------------------------------------------------------------- 1 | get('/hello', function() { 8 | return 'Hello!'; 9 | }); 10 | 11 | $app->run(); 12 | -------------------------------------------------------------------------------- /bin/skeleton/slim_composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "silex/silex": "1.0.*@dev" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "silex/silex", 3 | "description": "The PHP micro-framework based on the Symfony2 Components", 4 | "keywords": ["microframework"], 5 | "homepage": "http://silex.sensiolabs.org", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Fabien Potencier", 10 | "email": "fabien@symfony.com" 11 | }, 12 | { 13 | "name": "Igor Wiedler", 14 | "email": "igor@wiedler.ch" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=5.3.3", 19 | "pimple/pimple": "1.*", 20 | "symfony/event-dispatcher": ">=2.1,<2.3-dev", 21 | "symfony/http-foundation": ">=2.1,<2.3-dev", 22 | "symfony/http-kernel": ">=2.1,<2.3-dev", 23 | "symfony/routing": ">=2.1,<2.3-dev" 24 | }, 25 | "require-dev": { 26 | "symfony/security": ">=2.1,<2.3-dev", 27 | "symfony/config": ">=2.1,<2.3-dev", 28 | "symfony/locale": ">=2.1,<2.3-dev", 29 | "symfony/form": ">=2.1,<2.3-dev", 30 | "symfony/browser-kit": ">=2.1,<2.3-dev", 31 | "symfony/css-selector": ">=2.1,<2.3-dev", 32 | "symfony/dom-crawler": ">=2.1,<2.3-dev", 33 | "symfony/finder": ">=2.1,<2.3-dev", 34 | "symfony/monolog-bridge": ">=2.1,<2.3-dev", 35 | "symfony/options-resolver": ">=2.1,<2.3-dev", 36 | "symfony/process": ">=2.1,<2.3-dev", 37 | "symfony/serializer": ">=2.1,<2.3-dev", 38 | "symfony/translation": ">=2.1,<2.3-dev", 39 | "symfony/twig-bridge": ">=2.1,<2.3-dev", 40 | "symfony/validator": ">=2.1,<2.3-dev", 41 | "twig/twig": ">=1.8.0,<2.0-dev", 42 | "doctrine/dbal": ">=2.2.0,<2.4.0-dev", 43 | "swiftmailer/swiftmailer": "4.2.*" 44 | }, 45 | "suggest": { 46 | "symfony/browser-kit": ">=2.1,<2.3-dev", 47 | "symfony/css-selector": ">=2.1,<2.3-dev", 48 | "symfony/dom-crawler": ">=2.1,<2.3-dev" 49 | }, 50 | "autoload": { 51 | "psr-0": { "Silex": "src/" } 52 | }, 53 | "extra": { 54 | "branch-alias": { 55 | "dev-master": "1.0.x-dev" 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | from sphinx.highlighting import lexers 3 | from pygments.lexers.web import PhpLexer 4 | 5 | sys.path.append(os.path.abspath('_exts')) 6 | 7 | extensions = [] 8 | master_doc = 'index' 9 | highlight_language = 'php' 10 | 11 | project = u'Silex' 12 | copyright = u'2010 Fabien Potencier' 13 | 14 | version = '0' 15 | release = '0.0.0' 16 | 17 | lexers['php'] = PhpLexer(startinline=True) 18 | -------------------------------------------------------------------------------- /doc/contributing.rst: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | We are open to contributions to the Silex code. If you find 5 | a bug or want to contribute a provider, just follow these 6 | steps. 7 | 8 | * Fork `the Silex repository `_ 9 | on github. 10 | 11 | * Make your feature addition or bug fix. 12 | 13 | * Add tests for it. This is important so we don't break it in a future version unintentionally. 14 | 15 | * Send a pull request. Bonus points for topic branches. 16 | 17 | If you have a big change or would like to discuss something, 18 | please join us on the `mailing list 19 | `_. 20 | 21 | .. note:: 22 | 23 | Any code you contribute must be licensed under the MIT 24 | License. 25 | -------------------------------------------------------------------------------- /doc/cookbook/error_handler.rst: -------------------------------------------------------------------------------- 1 | How to convert errors to exceptions 2 | =================================== 3 | 4 | Silex will catch exceptions that are thrown from within a request/response 5 | cycle. It will however *not* catch PHP errors and notices. You can catch them 6 | by converting them to exceptions, this recipe will tell you how. 7 | 8 | Why does Silex not do this? 9 | --------------------------- 10 | 11 | Silex could do this automatically in theory, but there is a reason why it does 12 | not. Silex acts as a library, this means that it does not mess with any global 13 | state. Since error handlers are global in PHP, it is your responsibility as a 14 | user to register them. 15 | 16 | Registering the ErrorHandler 17 | ---------------------------- 18 | 19 | Fortunately, Silex ships with an ``ErrorHandler`` (it's part of the 20 | ``HttpKernel`` package) that solves this issue. It converts all errors to 21 | exceptions, and exceptions can be caught by Silex. 22 | 23 | You register it by calling the static ``register`` method:: 24 | 25 | use Symfony\Component\HttpKernel\Debug\ErrorHandler; 26 | 27 | ErrorHandler::register(); 28 | 29 | It is recommended that you do this in your front controller, i.e. 30 | ``web/index.php``. 31 | 32 | .. note:: 33 | 34 | The ``ErrorHandler`` has nothing to do with the ``ExceptionHandler``. The 35 | ``ExceptionHandler`` is responsible for displaying caught exceptions 36 | nicely. 37 | -------------------------------------------------------------------------------- /doc/cookbook/form_no_csrf.rst: -------------------------------------------------------------------------------- 1 | Disable CSRF Protection on a form using the FormExtension 2 | ========================================================= 3 | 4 | The *FormExtension* provides a service for building form in your application 5 | with the Symfony2 Form component. By default, the *FormExtension* uses the 6 | CSRF Protection avoiding Cross-site request forgery, a method by which a 7 | malicious user attempts to make your legitimate users unknowingly submit data 8 | that they don't intend to submit. 9 | 10 | You can find more details about CSRF Protection and CSRF token in the 11 | `Symfony2 Book 12 | `_. 13 | 14 | In some cases (for example, when embedding a form in an html email) you might 15 | want not to use this protection. The easiest way to avoid this is to 16 | understand that it is possible to give specific options to your form builder 17 | through the ``createBuilder()`` function. 18 | 19 | Example 20 | ------- 21 | 22 | .. code-block:: php 23 | 24 | $form = $app['form.factory']->createBuilder('form', null, array('csrf_protection' => false)); 25 | 26 | That's it, your form could be submitted from everywhere without CSRF Protection. 27 | 28 | Going further 29 | ------------- 30 | 31 | This specific example showed how to change the ``csrf_protection`` in the 32 | ``$options`` parameter of the ``createBuilder()`` function. More of them could 33 | be passed through this parameter, it is as simple as using the Symfony2 34 | ``getDefaultOptions()`` method in your form classes. `See more here 35 | `_. 36 | -------------------------------------------------------------------------------- /doc/cookbook/index.rst: -------------------------------------------------------------------------------- 1 | Cookbook 2 | ======== 3 | 4 | The cookbook section contains recipes for solving specific problems. 5 | 6 | .. toctree:: 7 | :maxdepth: 1 8 | :hidden: 9 | 10 | json_request_body 11 | translating_validation_messages 12 | session_storage 13 | form_no_csrf 14 | validator_yaml 15 | sub_requests 16 | error_handler 17 | 18 | Recipes 19 | ------- 20 | 21 | * :doc:`Accepting a JSON request body ` A common need when 22 | building a restful API is the ability to accept a JSON encoded entity from 23 | the request body. 24 | 25 | * :doc:`Translating Validation Messages`. 26 | 27 | * :doc:`How to use PdoSessionStorage to store sessions in the database 28 | `. 29 | 30 | * :doc:`How to disable the CSRF Protection on a form using the FormExtension 31 | `. 32 | 33 | * :doc:`How to use YAML to configure validation `. 34 | 35 | * :doc:`How to make sub-requests `. 36 | 37 | * :doc:`How to convert errors to exceptions `. 38 | -------------------------------------------------------------------------------- /doc/cookbook/json_request_body.rst: -------------------------------------------------------------------------------- 1 | Accepting a JSON request body 2 | ============================= 3 | 4 | A common need when building a restful API is the ability to accept a JSON 5 | encoded entity from the request body. 6 | 7 | An example for such an API could be a blog post creation. 8 | 9 | Example API 10 | ----------- 11 | 12 | In this example we will create an API for creating a blog post. The following 13 | is a spec of how we want it to work. 14 | 15 | Request 16 | ~~~~~~~ 17 | 18 | In the request we send the data for the blog post as a JSON object. We also 19 | indicate that using the ``Content-Type`` header: 20 | 21 | .. code-block:: text 22 | 23 | POST /blog/posts 24 | Accept: application/json 25 | Content-Type: application/json 26 | Content-Length: 57 27 | 28 | {"title":"Hello World!","body":"This is my first post!"} 29 | 30 | Response 31 | ~~~~~~~~ 32 | 33 | The server responds with a 201 status code, telling us that the post was 34 | created. It tells us the ``Content-Type`` of the response, which is also 35 | JSON: 36 | 37 | .. code-block:: text 38 | 39 | HTTP/1.1 201 Created 40 | Content-Type: application/json 41 | Content-Length: 65 42 | Connection: close 43 | 44 | {"id":"1","title":"Hello World!","body":"This is my first post!"} 45 | 46 | Parsing the request body 47 | ------------------------ 48 | 49 | The request body should only be parsed as JSON if the ``Content-Type`` header 50 | begins with ``application/json``. Since we want to do this for every request, 51 | the easiest solution is to use an application before middleware. 52 | 53 | We simply use ``json_decode`` to parse the content of the request and then 54 | replace the request data on the ``$request`` object:: 55 | 56 | use Symfony\Component\HttpFoundation\Request; 57 | use Symfony\Component\HttpFoundation\ParameterBag; 58 | 59 | $app->before(function (Request $request) { 60 | if (0 === strpos($request->headers->get('Content-Type'), 'application/json')) { 61 | $data = json_decode($request->getContent(), true); 62 | $request->request->replace(is_array($data) ? $data : array()); 63 | } 64 | }); 65 | 66 | Controller implementation 67 | ------------------------- 68 | 69 | Our controller will create a new blog post from the data provided and will 70 | return the post object, including its ``id``, as JSON:: 71 | 72 | use Symfony\Component\HttpFoundation\Request; 73 | use Symfony\Component\HttpFoundation\Response; 74 | 75 | $app->post('/blog/posts', function (Request $request) use ($app) { 76 | $post = array( 77 | 'title' => $request->request->get('title'), 78 | 'body' => $request->request->get('body'), 79 | ); 80 | 81 | $post['id'] = createPost($post); 82 | 83 | return $app->json($post, 201); 84 | }); 85 | 86 | Manual testing 87 | -------------- 88 | 89 | In order to manually test our API, we can use the ``curl`` command line 90 | utility, which allows sending HTTP requests: 91 | 92 | .. code-block:: bash 93 | 94 | $ curl http://blog.lo/blog/posts -d '{"title":"Hello World!","body":"This is my first post!"}' -H 'Content-Type: application/json' 95 | {"id":"1","title":"Hello World!","body":"This is my first post!"} 96 | -------------------------------------------------------------------------------- /doc/cookbook/session_storage.rst: -------------------------------------------------------------------------------- 1 | How to use PdoSessionStorage to store sessions in the database 2 | ============================================================== 3 | 4 | By default, the :doc:`SessionServiceProvider ` writes 5 | session information in files using Symfony2 NativeFileSessionStorage. Most 6 | medium to large websites use a database to store sessions instead of files, 7 | because databases are easier to use and scale in a multi-webserver 8 | environment. 9 | 10 | Symfony2's `NativeSessionStorage 11 | `_ 12 | has multiple storage handlers and one of them uses PDO to store sessions, 13 | `PdoSessionHandler 14 | `_. 15 | To use it, replace the ``session.storage.handler`` service in your application 16 | like explained below. 17 | 18 | With a dedicated PDO service 19 | ---------------------------- 20 | 21 | .. code-block:: php 22 | 23 | use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; 24 | 25 | $app->register(new Silex\Provider\SessionServiceProvider()); 26 | 27 | $app['pdo.dsn'] = 'mysql:dbname=mydatabase'; 28 | $app['pdo.user'] = 'myuser'; 29 | $app['pdo.password'] = 'mypassword'; 30 | 31 | $app['session.db_options'] = array( 32 | 'db_table' => 'session', 33 | 'db_id_col' => 'session_id', 34 | 'db_data_col' => 'session_value', 35 | 'db_time_col' => 'session_time', 36 | ); 37 | 38 | $app['pdo'] = $app->share(function () use ($app) { 39 | return new PDO( 40 | $app['pdo.dsn'], 41 | $app['pdo.user'], 42 | $app['pdo.password'] 43 | ); 44 | }); 45 | 46 | $app['session.storage.handler'] = $app->share(function () use ($app) { 47 | return new PdoSessionHandler( 48 | $app['pdo'], 49 | $app['session.db_options'], 50 | $app['session.storage.options'] 51 | ); 52 | }); 53 | 54 | Using the DoctrineServiceProvider 55 | --------------------------------- 56 | 57 | When using the :doc:`DoctrineServiceProvider ` You don't 58 | have to make another database connection, simply pass the getWrappedConnection method. 59 | 60 | .. code-block:: php 61 | 62 | use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; 63 | 64 | $app->register(new Silex\Provider\SessionServiceProvider()); 65 | 66 | $app['session.db_options'] = array( 67 | 'db_table' => 'session', 68 | 'db_id_col' => 'session_id', 69 | 'db_data_col' => 'session_value', 70 | 'db_time_col' => 'session_time', 71 | ); 72 | 73 | $app['session.storage.handler'] = $app->share(function () use ($app) { 74 | return new PdoSessionHandler( 75 | $app['db']->getWrappedConnection(), 76 | $app['session.db_options'], 77 | $app['session.storage.options'] 78 | ); 79 | }); 80 | 81 | Database structure 82 | ------------------ 83 | 84 | PdoSessionStorage needs a database table with 3 columns: 85 | 86 | * ``session_id``: ID column (VARCHAR(255) or larger) 87 | * ``session_value``: Value column (TEXT or CLOB) 88 | * ``session_time``: Time column (INTEGER) 89 | 90 | You can find examples of SQL statements to create the session table in the 91 | `Symfony2 cookbook 92 | `_ 93 | -------------------------------------------------------------------------------- /doc/cookbook/translating_validation_messages.rst: -------------------------------------------------------------------------------- 1 | Translating Validation Messages 2 | =============================== 3 | 4 | When working with Symfony2 validator, a common task would be to show localized 5 | validation messages. 6 | 7 | In order to do that, you will need to register translator and point to 8 | translated resources:: 9 | 10 | $app->register(new Silex\Provider\TranslationServiceProvider(), array( 11 | 'locale' => 'sr_Latn', 12 | 'translator.domains' => array(), 13 | )); 14 | 15 | $app->before(function () use ($app) { 16 | $app['translator']->addLoader('xlf', new Symfony\Component\Translation\Loader\XliffFileLoader()); 17 | $app['translator']->addResource('xlf', __DIR__.'/vendor/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.sr_Latn.xlf', 'sr_Latn', 'validators'); 18 | }); 19 | 20 | And that's all you need to load translations from Symfony2 ``xlf`` files. 21 | -------------------------------------------------------------------------------- /doc/cookbook/validator_yaml.rst: -------------------------------------------------------------------------------- 1 | How to use YAML to configure validation 2 | ======================================= 3 | 4 | Simplicity is at the heart of Silex so there is no out of the box solution to 5 | use YAML files for validation. But this doesn't mean that this is not 6 | possible. Let's see how to do it. 7 | 8 | First, you need to install the YAML Component. Declare it as a dependency in 9 | your ``composer.json`` file: 10 | 11 | .. code-block:: json 12 | 13 | "require": { 14 | "symfony/yaml": "2.1.*" 15 | } 16 | 17 | Next, you need to tell the Validation Service that you are not using 18 | ``StaticMethodLoader`` to load your class metadata but a YAML file:: 19 | 20 | $app->register(new ValidatorServiceProvider()); 21 | 22 | $app['validator.mapping.class_metadata_factory'] = new Symfony\Component\Validator\Mapping\ClassMetadataFactory( 23 | new Symfony\Component\Validator\Mapping\Loader\YamlFileLoader(__DIR__.'/validation.yml') 24 | ); 25 | 26 | Now, we can replace the usage of the static method and move all the validation 27 | rules to ``validation.yml``: 28 | 29 | .. code-block:: yaml 30 | 31 | # validation.yml 32 | Post: 33 | properties: 34 | title: 35 | - NotNull: ~ 36 | - NotBlank: ~ 37 | body: 38 | - Min: 100 39 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | Silex 2 | ===== 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | 7 | intro 8 | usage 9 | middlewares 10 | organizing_controllers 11 | services 12 | providers 13 | testing 14 | cookbook/index 15 | internals 16 | contributing 17 | providers/index 18 | web_servers 19 | changelog 20 | phar 21 | -------------------------------------------------------------------------------- /doc/internals.rst: -------------------------------------------------------------------------------- 1 | Internals 2 | ========= 3 | 4 | This chapter will tell you a bit about how Silex works 5 | internally. 6 | 7 | Silex 8 | ----- 9 | 10 | Application 11 | ~~~~~~~~~~~ 12 | 13 | The application is the main interface to Silex. It implements Symfony2's 14 | `HttpKernelInterface 15 | `_, 16 | so you can pass a `Request 17 | `_ 18 | to the ``handle`` method and it will return a `Response 19 | `_. 20 | 21 | It extends the ``Pimple`` service container, allowing for flexibility on the 22 | outside as well as the inside. You could replace any service, and you are also 23 | able to read them. 24 | 25 | The application makes strong use of the `EventDispatcher 26 | `_ 27 | to hook into the Symfony2 `HttpKernel 28 | `_ 29 | events. This allows fetching the ``Request``, converting string responses into 30 | ``Response`` objects and handling Exceptions. We also use it to dispatch some 31 | custom events like before/after middlewares and errors. 32 | 33 | Controller 34 | ~~~~~~~~~~ 35 | 36 | The Symfony2 `Route 37 | `_ is 38 | actually quite powerful. Routes can be named, which allows for URL generation. 39 | They can also have requirements for the variable parts. In order to allow 40 | settings these through a nice interface the ``match`` method (which is used by 41 | ``get``, ``post``, etc.) returns an instance of the ``Controller``, which 42 | wraps a route. 43 | 44 | ControllerCollection 45 | ~~~~~~~~~~~~~~~~~~~~ 46 | 47 | One of the goals of exposing the `RouteCollection 48 | `_ 49 | was to make it mutable, so providers could add stuff to it. The challenge here 50 | is the fact that routes know nothing about their name. The name only has 51 | meaning in context of the ``RouteCollection`` and cannot be changed. 52 | 53 | To solve this challenge we came up with a staging area for routes. The 54 | ``ControllerCollection`` holds the controllers until ``flush`` is called, at 55 | which point the routes are added to the ``RouteCollection``. Also, the 56 | controllers are then frozen. This means that they can no longer be modified 57 | and will throw an Exception if you try to do so. 58 | 59 | Unfortunately no good way for flushing implicitly could be found, which is why 60 | flushing is now always explicit. The Application will flush, but if you want 61 | to read the ``ControllerCollection`` before the request takes place, you will 62 | have to call flush yourself. 63 | 64 | The ``Application`` provides a shortcut ``flush`` method for flushing the 65 | ``ControllerCollection``. 66 | 67 | .. tip:: 68 | 69 | Instead of creating an instance of ``RouteCollection`` yourself, use the 70 | ``$app['controllers_factory']`` factory instead. 71 | 72 | Symfony2 73 | -------- 74 | 75 | Following Symfony2 components are used by Silex: 76 | 77 | * **HttpFoundation**: For ``Request`` and ``Response``. 78 | 79 | * **HttpKernel**: Because we need a heart. 80 | 81 | * **Routing**: For matching defined routes. 82 | 83 | * **EventDispatcher**: For hooking into the HttpKernel. 84 | 85 | For more information, `check out the Symfony website `_. 86 | -------------------------------------------------------------------------------- /doc/intro.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | Silex is a PHP microframework for PHP 5.3. It is built on the shoulders of 5 | Symfony2 and Pimple and also inspired by sinatra. 6 | 7 | A microframework provides the guts for building simple single-file apps. Silex 8 | aims to be: 9 | 10 | * *Concise*: Silex exposes an intuitive and concise API that is fun to use. 11 | 12 | * *Extensible*: Silex has an extension system based around the Pimple micro 13 | service-container that makes it even easier to tie in third party libraries. 14 | 15 | * *Testable*: Silex uses Symfony2's HttpKernel which abstracts request and 16 | response. This makes it very easy to test apps and the framework itself. It 17 | also respects the HTTP specification and encourages its proper use. 18 | 19 | In a nutshell, you define controllers and map them to routes, all in one step. 20 | 21 | **Let's go!**:: 22 | 23 | // web/index.php 24 | 25 | require_once __DIR__.'/../vendor/autoload.php'; 26 | 27 | $app = new Silex\Application(); 28 | 29 | $app->get('/hello/{name}', function ($name) use ($app) { 30 | return 'Hello '.$app->escape($name); 31 | }); 32 | 33 | $app->run(); 34 | 35 | All that is needed to get access to the Framework is to include the 36 | autoloader. 37 | 38 | Next we define a route to ``/hello/{name}`` that matches for ``GET`` requests. 39 | When the route matches, the function is executed and the return value is sent 40 | back to the client. 41 | 42 | Finally, the app is run. Visit ``/hello/world`` to see the result. It's really 43 | that easy! 44 | 45 | Installing Silex is as easy as it can get. `Download`_ the archive file, 46 | extract it, and you're done! 47 | 48 | .. _Download: http://silex.sensiolabs.org/download 49 | -------------------------------------------------------------------------------- /doc/organizing_controllers.rst: -------------------------------------------------------------------------------- 1 | Organizing Controllers 2 | ====================== 3 | 4 | When your application starts to define too many controllers, you might want to 5 | group them logically:: 6 | 7 | // define controllers for a blog 8 | $blog = $app['controllers_factory']; 9 | $blog->get('/', function () { 10 | return 'Blog home page'; 11 | }); 12 | // ... 13 | 14 | // define controllers for a forum 15 | $forum = $app['controllers_factory']; 16 | $forum->get('/', function () { 17 | return 'Forum home page'; 18 | }); 19 | 20 | // define "global" controllers 21 | $app->get('/', function () { 22 | return 'Main home page'; 23 | }); 24 | 25 | $app->mount('/blog', $blog); 26 | $app->mount('/forum', $forum); 27 | 28 | .. note:: 29 | 30 | ``$app['controllers_factory']`` is a factory that returns a new instance 31 | of ``ControllerCollection`` when used. 32 | 33 | ``mount()`` prefixes all routes with the given prefix and merges them into the 34 | main Application. So, ``/`` will map to the main home page, ``/blog/`` to the 35 | blog home page, and ``/forum/`` to the forum home page. 36 | 37 | .. caution:: 38 | 39 | When mounting a route collection under ``/blog``, it is not possible to 40 | define a route for the ``/blog`` URL. The shortest possible URL is 41 | ``/blog/``. 42 | 43 | .. note:: 44 | 45 | When calling ``get()``, ``match()``, or any other HTTP methods on the 46 | Application, you are in fact calling them on a default instance of 47 | ``ControllerCollection`` (stored in ``$app['controllers']``). 48 | 49 | Another benefit is the ability to apply settings on a set of controllers very 50 | easily. Building on the example from the middleware section, here is how you 51 | would secure all controllers for the backend collection:: 52 | 53 | $backend = $app['controllers_factory']; 54 | 55 | // ensure that all controllers require logged-in users 56 | $backend->before($mustBeLogged); 57 | 58 | .. tip:: 59 | 60 | For a better readability, you can split each controller collection into a 61 | separate file:: 62 | 63 | // blog.php 64 | $blog = $app['controllers_factory']; 65 | $blog->get('/', function () { return 'Blog home page'; }); 66 | 67 | return $blog; 68 | 69 | // app.php 70 | $app->mount('/blog', include 'blog.php'); 71 | 72 | Instead of requiring a file, you can also create a :doc:`Controller 73 | provider `. 74 | -------------------------------------------------------------------------------- /doc/phar.rst: -------------------------------------------------------------------------------- 1 | Phar File 2 | ========= 3 | 4 | .. caution:: 5 | 6 | Using the Silex ``phar`` file is deprecated. You should use Composer 7 | instead to install Silex and its dependencies or download one of the 8 | archives. 9 | 10 | Installing 11 | ---------- 12 | 13 | Installing Silex is as easy as downloading the `phar 14 | `_ and storing it somewhere on 15 | the disk. Then, require it in your script:: 16 | 17 | get('/hello/{name}', function ($name) use ($app) { 24 | return 'Hello '.$app->escape($name); 25 | }); 26 | 27 | $app->run(); 28 | 29 | Console 30 | ------- 31 | 32 | Silex includes a lightweight console for updating to the latest version. 33 | 34 | To find out which version of Silex you are using, invoke ``silex.phar`` on the 35 | command-line with ``version`` as an argument: 36 | 37 | .. code-block:: text 38 | 39 | $ php silex.phar version 40 | Silex version 0a243d3 2011-04-17 14:49:31 +0200 41 | 42 | To check that your are using the latest version, run the ``check`` command: 43 | 44 | .. code-block:: text 45 | 46 | $ php silex.phar check 47 | 48 | To update ``silex.phar`` to the latest version, invoke the ``update`` 49 | command: 50 | 51 | .. code-block:: text 52 | 53 | $ php silex.phar update 54 | 55 | This will automatically download a new ``silex.phar`` from 56 | ``silex.sensiolabs.org`` and replace the existing one. 57 | 58 | Pitfalls 59 | -------- 60 | 61 | There are some things that can go wrong. Here we will try and outline the 62 | most frequent ones. 63 | 64 | PHP configuration 65 | ~~~~~~~~~~~~~~~~~ 66 | 67 | Certain PHP distributions have restrictive default Phar settings. Setting 68 | the following may help. 69 | 70 | .. code-block:: ini 71 | 72 | detect_unicode = Off 73 | phar.readonly = Off 74 | phar.require_hash = Off 75 | 76 | If you are on Suhosin you will also have to set this: 77 | 78 | .. code-block:: ini 79 | 80 | suhosin.executor.include.whitelist = phar 81 | 82 | .. note:: 83 | 84 | Ubuntu's PHP ships with Suhosin, so if you are using Ubuntu, you will need 85 | this change. 86 | 87 | Phar-Stub bug 88 | ~~~~~~~~~~~~~ 89 | 90 | Some PHP installations have a bug that throws a ``PharException`` when trying 91 | to include the Phar. It will also tell you that ``Silex\Application`` could not 92 | be found. A workaround is using the following include line:: 93 | 94 | require_once 'phar://'.__DIR__.'/silex.phar/autoload.php'; 95 | 96 | The exact cause of this issue could not be determined yet. 97 | 98 | ioncube loader bug 99 | ~~~~~~~~~~~~~~~~~~ 100 | 101 | Ioncube loader is an extension that can decode PHP encoded file. 102 | Unfortunately, old versions (prior to version 4.0.9) are not working well 103 | with phar archives. 104 | You must either upgrade Ioncube loader to version 4.0.9 or newer or disable it 105 | by commenting or removing this line in your php.ini file: 106 | 107 | .. code-block:: ini 108 | 109 | zend_extension = /usr/lib/php5/20090626+lfs/ioncube_loader_lin_5.3.so 110 | -------------------------------------------------------------------------------- /doc/providers/http_cache.rst: -------------------------------------------------------------------------------- 1 | HttpCacheServiceProvider 2 | ======================== 3 | 4 | The *HttpCacheProvider* provides support for the Symfony2 Reverse Proxy. 5 | 6 | Parameters 7 | ---------- 8 | 9 | * **http_cache.cache_dir**: The cache directory to store the HTTP cache data. 10 | 11 | * **http_cache.options** (optional): An array of options for the `HttpCache 12 | `_ 13 | constructor. 14 | 15 | Services 16 | -------- 17 | 18 | * **http_cache**: An instance of `HttpCache 19 | `_. 20 | 21 | * **http_cache.esi**: An instance of `Esi 22 | `_, 23 | that implements the ESI capabilities to Request and Response instances. 24 | 25 | * **http_cache.store**: An instance of `Store 26 | `_, 27 | that implements all the logic for storing cache metadata (Request and Response 28 | headers). 29 | 30 | Registering 31 | ----------- 32 | 33 | .. code-block:: php 34 | 35 | $app->register(new Silex\Provider\HttpCacheServiceProvider(), array( 36 | 'http_cache.cache_dir' => __DIR__.'/cache/', 37 | )); 38 | 39 | Usage 40 | ----- 41 | 42 | Silex already supports any reverse proxy like Varnish out of the box by 43 | setting Response HTTP cache headers:: 44 | 45 | use Symfony\Component\HttpFoundation\Response; 46 | 47 | $app->get('/', function() { 48 | return new Response('Foo', 200, array( 49 | 'Cache-Control' => 's-maxage=5', 50 | )); 51 | }); 52 | 53 | .. tip:: 54 | 55 | If you want Silex to trust the ``X-Forwarded-For*`` headers from your 56 | reverse proxy, you will need to run your application like this:: 57 | 58 | use Symfony\Component\HttpFoundation\Request; 59 | 60 | Request::trustProxyData(); 61 | $app->run(); 62 | 63 | This provider allows you to use the Symfony2 reverse proxy natively with 64 | Silex applications by using the ``http_cache`` service:: 65 | 66 | $app['http_cache']->run(); 67 | 68 | The provider also provides ESI support:: 69 | 70 | $app->get('/', function() { 71 | $response = new Response(<< 73 | 74 | Hello 75 | 76 | 77 | 78 | 79 | EOF 80 | , 200, array( 81 | 'Surrogate-Control' => 'content="ESI/1.0"', 82 | )); 83 | 84 | $response->setTtl(20); 85 | 86 | return $response; 87 | }); 88 | 89 | $app->get('/included', function() { 90 | $response = new Response('Foo'); 91 | $response->setTtl(5); 92 | 93 | return $response; 94 | }); 95 | 96 | $app['http_cache']->run(); 97 | 98 | If your application doesn't use ESI, you can disable it to slightly improve the 99 | overall performance:: 100 | 101 | $app->register(new Silex\Provider\HttpCacheServiceProvider(), array( 102 | 'http_cache.cache_dir' => __DIR__.'/cache/', 103 | 'http_cache.esi' => null, 104 | )); 105 | 106 | .. tip:: 107 | 108 | To help you debug caching issues, set your application ``debug`` to true. 109 | Symfony automatically adds a ``X-Symfony-Cache`` header to each response 110 | with useful information about cache hits and misses. 111 | 112 | If you are *not* using the Symfony Session provider, you might want to set 113 | the PHP ``session.cache_limiter`` setting to an empty value to avoid the 114 | default PHP behavior. 115 | 116 | Finally, check that your Web server does not override your caching strategy. 117 | 118 | For more information, consult the `Symfony2 HTTP Cache documentation 119 | `_. 120 | -------------------------------------------------------------------------------- /doc/providers/index.rst: -------------------------------------------------------------------------------- 1 | Silex 2 | ===== 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | 7 | doctrine 8 | monolog 9 | session 10 | swiftmailer 11 | translation 12 | twig 13 | url_generator 14 | validator 15 | form 16 | http_cache 17 | security 18 | serializer 19 | service_controller 20 | -------------------------------------------------------------------------------- /doc/providers/monolog.rst: -------------------------------------------------------------------------------- 1 | MonologServiceProvider 2 | ====================== 3 | 4 | The *MonologServiceProvider* provides a default logging mechanism through 5 | Jordi Boggiano's `Monolog `_ library. 6 | 7 | It will log requests and errors and allow you to add logging to your 8 | application. This allows you to debug and monitor the behaviour, 9 | even in production. 10 | 11 | Parameters 12 | ---------- 13 | 14 | * **monolog.logfile**: File where logs are written to. 15 | 16 | * **monolog.level** (optional): Level of logging defaults 17 | to ``DEBUG``. Must be one of ``Logger::DEBUG``, ``Logger::INFO``, 18 | ``Logger::WARNING``, ``Logger::ERROR``. ``DEBUG`` will log 19 | everything, ``INFO`` will log everything except ``DEBUG``, 20 | etc. 21 | 22 | * **monolog.name** (optional): Name of the monolog channel, 23 | defaults to ``myapp``. 24 | 25 | Services 26 | -------- 27 | 28 | * **monolog**: The monolog logger instance. 29 | 30 | Example usage:: 31 | 32 | $app['monolog']->addDebug('Testing the Monolog logging.'); 33 | 34 | Registering 35 | ----------- 36 | 37 | .. code-block:: php 38 | 39 | $app->register(new Silex\Provider\MonologServiceProvider(), array( 40 | 'monolog.logfile' => __DIR__.'/development.log', 41 | )); 42 | 43 | .. note:: 44 | 45 | Monolog comes with the "fat" Silex archive but not with the regular one. 46 | If you are using Composer, add it as a dependency to your 47 | ``composer.json`` file: 48 | 49 | .. code-block:: json 50 | 51 | "require": { 52 | "monolog/monolog": ">=1.0.0", 53 | } 54 | 55 | Usage 56 | ----- 57 | 58 | The MonologServiceProvider provides a ``monolog`` service. You can use it to 59 | add log entries for any logging level through ``addDebug()``, ``addInfo()``, 60 | ``addWarning()`` and ``addError()``:: 61 | 62 | use Symfony\Component\HttpFoundation\Response; 63 | 64 | $app->post('/user', function () use ($app) { 65 | // ... 66 | 67 | $app['monolog']->addInfo(sprintf("User '%s' registered.", $username)); 68 | 69 | return new Response('', 201); 70 | }); 71 | 72 | Customization 73 | ------------- 74 | 75 | You can configure Monolog (like adding or changing the handlers) before using 76 | it by extending the ``monolog`` service:: 77 | 78 | $app['monolog'] = $app->share($app->extend('monolog', function($monolog, $app) { 79 | $monolog->pushHandler(...); 80 | 81 | return $monolog; 82 | })); 83 | 84 | Traits 85 | ------ 86 | 87 | ``Silex\Application\MonologTrait`` adds the following shortcuts: 88 | 89 | * **log**: Logs a message. 90 | 91 | .. code-block:: php 92 | 93 | $app->log(sprintf("User '%s' registered.", $username)); 94 | 95 | For more information, check out the `Monolog documentation 96 | `_. 97 | -------------------------------------------------------------------------------- /doc/providers/serializer.rst: -------------------------------------------------------------------------------- 1 | SerializerServiceProvider 2 | =========================== 3 | 4 | The *SerializerServiceProvider* provides a service for serializing objects. 5 | 6 | Parameters 7 | ---------- 8 | 9 | None. 10 | 11 | Services 12 | -------- 13 | 14 | * **serializer**: An instance of `Symfony\Component\Serializer\Serializer 15 | `_. 16 | 17 | * **serializer.encoders**: `Symfony\Component\Serializer\Encoder\JsonEncoder 18 | `_ 19 | and `Symfony\Component\Serializer\Encoder\XmlEncoder 20 | `_. 21 | 22 | * **serializer.normalizers**: `Symfony\Component\Serializer\Normalizer\CustomNormalizer 23 | `_ 24 | and `Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer 25 | `_. 26 | 27 | Registering 28 | ----------- 29 | 30 | .. code-block:: php 31 | 32 | $app->register(new Silex\Provider\SerializerServiceProvider()); 33 | 34 | Usage 35 | ----- 36 | 37 | The ``SerializerServiceProvider`` provider provides a ``serializer`` service: 38 | 39 | .. code-block:: php 40 | 41 | use Silex\Application; 42 | use Silex\Provider\SerializerServiceProvider; 43 | use Symfony\Component\HttpFoundation\Response; 44 | 45 | $app = new Application(); 46 | 47 | $app->register(new SerializerServiceProvider()); 48 | 49 | // only accept content types supported by the serializer via the assert method. 50 | $app->get("/pages/{id}.{_format}", function ($id) use ($app) { 51 | // assume a page_repository service exists that returns Page objects. The 52 | // object returned has getters and setters exposing the state. 53 | $page = $app['page_repository']->find($id); 54 | $format = $app['request']->getRequestFormat(); 55 | 56 | if (!$page instanceof Page) { 57 | $app->abort("No page found for id: $id"); 58 | } 59 | 60 | return new Response($app['serializer']->serialize($page, $format), 200, array( 61 | "Content-Type" => $app['request']->getMimeType($format) 62 | )); 63 | })->assert("_format", "xml|json") 64 | ->assert("id", "\d+"); 65 | 66 | -------------------------------------------------------------------------------- /doc/providers/service_controller.rst: -------------------------------------------------------------------------------- 1 | ServiceControllerServiceProvider 2 | ================================ 3 | 4 | As your Silex application grows, you may wish to begin organizing your 5 | controllers in a more formal fashion. Silex can use controller classes out of 6 | the box, but with a bit of work, your controllers can be created as services, 7 | giving you the full power of dependency injection and lazy loading. 8 | 9 | .. ::todo Link above to controller classes cookbook 10 | 11 | Why would I want to do this? 12 | ---------------------------- 13 | 14 | - Dependency Injection over Service Location 15 | 16 | Using this method, you can inject the actual dependencies required by your 17 | controller and gain total inversion of control, while still maintaining the 18 | lazy loading of your controllers and it's dependencies. Because your 19 | dependencies are clearly defined, they are easily mocked, allowing you to test 20 | your controllers in isolation. 21 | 22 | - Framework Independence 23 | 24 | Using this method, your controllers start to become more independent of the 25 | framework you are using. Carefully crafted, your controllers will become 26 | reusable with multiple frameworks. By keeping careful control of your 27 | dependencies, your controllers could easily become compatible with Silex, 28 | Symfony (full stack) and Drupal, to name just a few. 29 | 30 | Parameters 31 | ---------- 32 | 33 | There are currently no parameters for the ``ServiceControllerServiceProvider``. 34 | 35 | Services 36 | -------- 37 | 38 | There are no extra services provided, the ``ServiceControllerServiceProvider`` 39 | simply extends the existing **resolver** service. 40 | 41 | Registering 42 | ----------- 43 | 44 | .. code-block:: php 45 | 46 | $app->register(new Silex\Provider\ServiceControllerServiceProvider()); 47 | 48 | Usage 49 | ----- 50 | 51 | In this slightly contrived example of a blog API, we're going to change the 52 | ``/posts.json`` route to use a controller, that is defined as a service. 53 | 54 | .. code-block:: php 55 | 56 | use Silex\Application; 57 | use Demo\Repository\PostRepository; 58 | 59 | $app = new Application(); 60 | 61 | $app['posts.repository'] = $app->share(function() { 62 | return new PostRepository; 63 | }); 64 | 65 | $app->get('/posts.json', function() use ($app) { 66 | return $app->json($app['posts.repository']->findAll()); 67 | }); 68 | 69 | Rewriting your controller as a service is pretty simple, create a Plain Ol' PHP 70 | Object with your ``PostRepository`` as a dependency, along with an 71 | ``indexJsonAction`` method to handle the request. Although not shown in the 72 | example below, you can use type hinting and parameter naming to get the 73 | parameters you need, just like with standard Silex routes. 74 | 75 | If you are a TDD/BDD fan (and you should be), you may notice that this 76 | controller has well defined responsibilities and dependencies, and is easily 77 | tested/specced. You may also notice that the only external dependency is on 78 | ``Symfony\Component\HttpFoundation\JsonResponse``, meaning this controller could 79 | easily be used in a Symfony (full stack) application, or potentially with other 80 | applications or frameworks that know how to handle a `Symfony/HttpFoundation 81 | `_ 82 | ``Response`` object. 83 | 84 | .. code-block:: php 85 | 86 | namespace Demo\Controller; 87 | 88 | use Demo\Repository\PostRepository; 89 | use Symfony\Component\HttpFoundation\JsonResponse; 90 | 91 | class PostController 92 | { 93 | protected $repo; 94 | 95 | public function __construct(PostRepository $repo) 96 | { 97 | $this->repo = $repo; 98 | } 99 | 100 | public function indexJsonAction() 101 | { 102 | return new JsonResponse($this->repo->findAll()); 103 | } 104 | } 105 | 106 | And lastly, define your controller as a service in the application, along with 107 | your route. The syntax in the route definition is the name of the service, 108 | followed by a single colon (:), followed by the method name. 109 | 110 | .. code-block:: php 111 | 112 | $app['posts.controller'] = $app->share(function() use ($app) { 113 | return new PostController($app['posts.repository']); 114 | }); 115 | 116 | $app->get('/posts.json', "posts.controller:indexJsonAction"); 117 | -------------------------------------------------------------------------------- /doc/providers/session.rst: -------------------------------------------------------------------------------- 1 | SessionServiceProvider 2 | ====================== 3 | 4 | The *SessionServiceProvider* provides a service for storing data persistently 5 | between requests. 6 | 7 | Parameters 8 | ---------- 9 | 10 | * **session.storage.save_path** (optional): The path for the 11 | ``NativeFileSessionHandler``, defaults to the value of 12 | ``sys_get_temp_dir()``. 13 | 14 | * **session.storage.options**: An array of options that is passed to the 15 | constructor of the ``session.storage`` service. 16 | 17 | In case of the default `NativeSessionStorage 18 | `_, 19 | the possible options are: 20 | 21 | * **name**: The cookie name (_SESS by default) 22 | * **id**: The session id (null by default) 23 | * **cookie_lifetime**: Cookie lifetime 24 | * **path**: Cookie path 25 | * **domain**: Cookie domain 26 | * **secure**: Cookie secure (HTTPS) 27 | * **httponly**: Whether the cookie is http only 28 | 29 | However, all of these are optional. Sessions last as long as the browser is 30 | open. To override this, set the ``lifetime`` option. 31 | 32 | * **session.test**: Whether to simulate sessions or not (useful when writing 33 | functional tests). 34 | 35 | Services 36 | -------- 37 | 38 | * **session**: An instance of Symfony2's `Session 39 | `_. 40 | 41 | * **session.storage**: A service that is used for persistence of the session 42 | data. 43 | 44 | * **session.storage.handler**: A service that is used by the 45 | ``session.storage`` for data access. Defaults to a `NativeFileSessionHandler 46 | `_ 47 | storage handler. 48 | 49 | Registering 50 | ----------- 51 | 52 | .. code-block:: php 53 | 54 | $app->register(new Silex\Provider\SessionServiceProvider()); 55 | 56 | Usage 57 | ----- 58 | 59 | The Session provider provides a ``session`` service. Here is an example that 60 | authenticates a user and creates a session for him:: 61 | 62 | use Symfony\Component\HttpFoundation\Response; 63 | 64 | $app->get('/login', function () use ($app) { 65 | $username = $app['request']->server->get('PHP_AUTH_USER', false); 66 | $password = $app['request']->server->get('PHP_AUTH_PW'); 67 | 68 | if ('igor' === $username && 'password' === $password) { 69 | $app['session']->set('user', array('username' => $username)); 70 | return $app->redirect('/account'); 71 | } 72 | 73 | $response = new Response(); 74 | $response->headers->set('WWW-Authenticate', sprintf('Basic realm="%s"', 'site_login')); 75 | $response->setStatusCode(401, 'Please sign in.'); 76 | return $response; 77 | }); 78 | 79 | $app->get('/account', function () use ($app) { 80 | if (null === $user = $app['session']->get('user')) { 81 | return $app->redirect('/login'); 82 | } 83 | 84 | return "Welcome {$user['username']}!"; 85 | }); 86 | 87 | 88 | Custom Session Configurations 89 | ----------------------------- 90 | 91 | If your system is using a custom session configuration (such as a redis handler 92 | from a PHP extension) then you need to disable the NativeFileSessionHandler by 93 | setting ``session.storage.handler`` to null. You will have to configure the 94 | ``session.save_path`` ini setting yourself in that case. 95 | 96 | .. code-block:: php 97 | 98 | $app['session.storage.handler'] = null; 99 | 100 | -------------------------------------------------------------------------------- /doc/providers/swiftmailer.rst: -------------------------------------------------------------------------------- 1 | SwiftmailerServiceProvider 2 | ========================== 3 | 4 | The *SwiftmailerServiceProvider* provides a service for sending email through 5 | the `Swift Mailer `_ library. 6 | 7 | You can use the ``mailer`` service to send messages easily. By default, it 8 | will attempt to send emails through SMTP. 9 | 10 | Parameters 11 | ---------- 12 | 13 | * **swiftmailer.options**: An array of options for the default SMTP-based 14 | configuration. 15 | 16 | The following options can be set: 17 | 18 | * **host**: SMTP hostname, defaults to 'localhost'. 19 | * **port**: SMTP port, defaults to 25. 20 | * **username**: SMTP username, defaults to an empty string. 21 | * **password**: SMTP password, defaults to an empty string. 22 | * **encryption**: SMTP encryption, defaults to null. 23 | * **auth_mode**: SMTP authentication mode, defaults to null. 24 | 25 | Example usage:: 26 | 27 | $app['swiftmailer.options'] = array( 28 | 'host' => 'host', 29 | 'port' => '25', 30 | 'username' => 'username', 31 | 'password' => 'password', 32 | 'encryption' => null, 33 | 'auth_mode' => null 34 | ); 35 | 36 | Services 37 | -------- 38 | 39 | * **mailer**: The mailer instance. 40 | 41 | Example usage:: 42 | 43 | $message = \Swift_Message::newInstance(); 44 | 45 | // ... 46 | 47 | $app['mailer']->send($message); 48 | 49 | * **swiftmailer.transport**: The transport used for e-mail 50 | delivery. Defaults to a ``Swift_Transport_EsmtpTransport``. 51 | 52 | * **swiftmailer.transport.buffer**: StreamBuffer used by 53 | the transport. 54 | 55 | * **swiftmailer.transport.authhandler**: Authentication 56 | handler used by the transport. Will try the following 57 | by default: CRAM-MD5, login, plaintext. 58 | 59 | * **swiftmailer.transport.eventdispatcher**: Internal event 60 | dispatcher used by Swiftmailer. 61 | 62 | Registering 63 | ----------- 64 | 65 | .. code-block:: php 66 | 67 | $app->register(new Silex\Provider\SwiftmailerServiceProvider()); 68 | 69 | .. note:: 70 | 71 | SwiftMailer comes with the "fat" Silex archive but not with the regular 72 | one. If you are using Composer, add it as a dependency to your 73 | ``composer.json`` file: 74 | 75 | .. code-block:: json 76 | 77 | "require": { 78 | "swiftmailer/swiftmailer": ">=4.1.2,<4.2-dev" 79 | } 80 | 81 | Usage 82 | ----- 83 | 84 | The Swiftmailer provider provides a ``mailer`` service:: 85 | 86 | $app->post('/feedback', function () use ($app) { 87 | $request = $app['request']; 88 | 89 | $message = \Swift_Message::newInstance() 90 | ->setSubject('[YourSite] Feedback') 91 | ->setFrom(array('noreply@yoursite.com')) 92 | ->setTo(array('feedback@yoursite.com')) 93 | ->setBody($request->get('message')); 94 | 95 | $app['mailer']->send($message); 96 | 97 | return new Response('Thank you for your feedback!', 201); 98 | }); 99 | 100 | Traits 101 | ------ 102 | 103 | ``Silex\Application\SwiftmailerTrait`` adds the following shortcuts: 104 | 105 | * **mail**: Sends an email. 106 | 107 | .. code-block:: php 108 | 109 | $app->mail(\Swift_Message::newInstance() 110 | ->setSubject('[YourSite] Feedback') 111 | ->setFrom(array('noreply@yoursite.com')) 112 | ->setTo(array('feedback@yoursite.com')) 113 | ->setBody($request->get('message'))); 114 | 115 | For more information, check out the `Swift Mailer documentation 116 | `_. 117 | -------------------------------------------------------------------------------- /doc/providers/url_generator.rst: -------------------------------------------------------------------------------- 1 | UrlGeneratorServiceProvider 2 | =========================== 3 | 4 | The *UrlGeneratorServiceProvider* provides a service for generating URLs for 5 | named routes. 6 | 7 | Parameters 8 | ---------- 9 | 10 | None. 11 | 12 | Services 13 | -------- 14 | 15 | * **url_generator**: An instance of `UrlGenerator 16 | `_, 17 | using the `RouteCollection 18 | `_ 19 | that is provided through the ``routes`` service. It has a ``generate`` 20 | method, which takes the route name as an argument, followed by an array of 21 | route parameters. 22 | 23 | Registering 24 | ----------- 25 | 26 | .. code-block:: php 27 | 28 | $app->register(new Silex\Provider\UrlGeneratorServiceProvider()); 29 | 30 | Usage 31 | ----- 32 | 33 | The UrlGenerator provider provides a ``url_generator`` service:: 34 | 35 | $app->get('/', function () { 36 | return 'welcome to the homepage'; 37 | }) 38 | ->bind('homepage'); 39 | 40 | $app->get('/hello/{name}', function ($name) { 41 | return "Hello $name!"; 42 | }) 43 | ->bind('hello'); 44 | 45 | $app->get('/navigation', function () use ($app) { 46 | return 'Home'. 47 | ' | '. 48 | 'Hello Igor'; 49 | }); 50 | 51 | When using Twig, the service can be used like this: 52 | 53 | .. code-block:: jinja 54 | 55 | {{ app.url_generator.generate('homepage') }} 56 | 57 | Moreover, if you use Twig, you will have access to the ``path()`` and 58 | ``url()`` functions: 59 | 60 | .. code-block:: jinja 61 | 62 | {{ path('homepage') }} 63 | {{ url('homepage') }} {# generates the absolute url http://example.org/ #} 64 | {{ path('hello', {name: 'Fabien'}) }} 65 | {{ url('hello', {name: 'Fabien'}) }} {# generates the absolute url http://example.org/hello/Fabien #} 66 | 67 | Traits 68 | ------ 69 | 70 | ``Silex\Application\UrlGeneratorTrait`` adds the following shortcuts: 71 | 72 | * **path**: Generates a path. 73 | 74 | * **url**: Generates an absolute URL. 75 | 76 | .. code-block:: php 77 | 78 | $app->path('homepage'); 79 | $app->url('homepage'); 80 | -------------------------------------------------------------------------------- /doc/web_servers.rst: -------------------------------------------------------------------------------- 1 | Webserver Configuration 2 | ======================= 3 | 4 | Apache 5 | ------ 6 | 7 | If you are using Apache you can use a ``.htaccess`` file for this: 8 | 9 | .. code-block:: apache 10 | 11 | 12 | Options -MultiViews 13 | 14 | RewriteEngine On 15 | #RewriteBase /path/to/app 16 | RewriteCond %{REQUEST_FILENAME} !-f 17 | RewriteRule ^ index.php [L] 18 | 19 | 20 | .. note:: 21 | 22 | If your site is not at the webroot level you will have to uncomment the 23 | ``RewriteBase`` statement and adjust the path to point to your directory, 24 | relative from the webroot. 25 | 26 | Alternatively, if you use Apache 2.2.16 or higher, you can use the 27 | `FallbackResource directive`_ so make your .htaccess even easier: 28 | 29 | .. code-block:: apache 30 | 31 | FallbackResource /index.php 32 | 33 | .. note:: 34 | 35 | If your site is not at the webroot level you will have to adjust the path to 36 | point to your directory, relative from the webroot. 37 | 38 | nginx 39 | ----- 40 | 41 | If you are using nginx, configure your vhost to forward non-existent 42 | resources to ``index.php``: 43 | 44 | .. code-block:: nginx 45 | 46 | server { 47 | #site root is redirected to the app boot script 48 | location = / { 49 | try_files @site @site; 50 | } 51 | 52 | #all other locations try other files first and go to our front controller if none of them exists 53 | location / { 54 | try_files $uri $uri/ @site; 55 | } 56 | 57 | #return 404 for all php files as we do have a front controller 58 | location ~ \.php$ { 59 | return 404; 60 | } 61 | 62 | location @site { 63 | fastcgi_pass unix:/var/run/php-fpm/www.sock; 64 | include fastcgi_params; 65 | fastcgi_param SCRIPT_FILENAME $document_root/index.php; 66 | #uncomment when running via https 67 | #fastcgi_param HTTPS on; 68 | } 69 | } 70 | 71 | IIS 72 | --- 73 | 74 | If you are using the Internet Information Services from Windows, you can use 75 | this sample ``web.config`` file: 76 | 77 | .. code-block:: xml 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | Lighttpd 103 | -------- 104 | 105 | If you are using lighttpd, use this sample ``simple-vhost`` as a starting 106 | point: 107 | 108 | .. code-block:: lighttpd 109 | 110 | server.document-root = "/path/to/app" 111 | 112 | url.rewrite-once = ( 113 | # configure some static files 114 | "^/assets/.+" => "$0", 115 | "^/favicon\.ico$" => "$0", 116 | 117 | "^(/[^\?]*)(\?.*)?" => "/index.php$1$2" 118 | ) 119 | 120 | .. _FallbackResource directive: http://www.adayinthelifeof.nl/2012/01/21/apaches-fallbackresource-your-new-htaccess-command/ 121 | 122 | PHP 5.4 123 | ------- 124 | 125 | PHP 5.4 ships with a built-in webserver for development. This server allows 126 | you to run silex without any configuration. However, in order to serve static 127 | files, you'll have to make sure your front controller returns false in that 128 | case:: 129 | 130 | // web/index.php 131 | 132 | $filename = __DIR__.preg_replace('#(\?.*)$#', '', $_SERVER['REQUEST_URI']); 133 | if (php_sapi_name() === 'cli-server' && is_file($filename)) { 134 | return false; 135 | } 136 | 137 | $app = require __DIR__.'/../src/app.php'; 138 | $app->run(); 139 | 140 | 141 | Assuming your front controller is at ``web/index.php``, you can start the 142 | server from the command-line with this command: 143 | 144 | .. code-block:: text 145 | 146 | $ php -S localhost:8080 -t web web/index.php 147 | 148 | Now the application should be running at ``http://localhost:8080``. 149 | 150 | .. note:: 151 | 152 | This server is for development only. It is **not** recommended to use it 153 | in production. 154 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | ./tests/Silex/ 17 | 18 | 19 | 20 | 21 | ./src 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Silex/Application/FormTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Application; 13 | 14 | use Symfony\Component\Form\FormBuilder; 15 | 16 | /** 17 | * Form trait. 18 | * 19 | * @author Fabien Potencier 20 | */ 21 | trait FormTrait 22 | { 23 | /** 24 | * Creates and returns a form builder instance 25 | * 26 | * @param mixed $data The initial data for the form 27 | * @param array $options Options for the form 28 | * 29 | * @return FormBuilder 30 | */ 31 | public function form($data = null, array $options = array()) 32 | { 33 | return $this['form.factory']->createBuilder('form', $data, $options); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Silex/Application/MonologTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Application; 13 | 14 | use Monolog\Logger; 15 | 16 | /** 17 | * Monolog trait. 18 | * 19 | * @author Fabien Potencier 20 | */ 21 | trait MonologTrait 22 | { 23 | /** 24 | * Adds a log record. 25 | * 26 | * @param string $message The log message 27 | * @param array $context The log context 28 | * @param integer $level The logging level 29 | * 30 | * @return Boolean Whether the record has been processed 31 | */ 32 | public function log($message, array $context = array(), $level = Logger::INFO) 33 | { 34 | return $this['monolog']->addRecord($level, $message, $context); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Silex/Application/SecurityTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Application; 13 | 14 | use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; 15 | use Symfony\Component\Security\Core\User\UserInterface; 16 | 17 | /** 18 | * Security trait. 19 | * 20 | * @author Fabien Potencier 21 | */ 22 | trait SecurityTrait 23 | { 24 | /** 25 | * Gets a user from the Security Context. 26 | * 27 | * @return mixed 28 | * 29 | * @see TokenInterface::getUser() 30 | */ 31 | public function user() 32 | { 33 | if (null === $token = $this['security']->getToken()) { 34 | return null; 35 | } 36 | 37 | if (!is_object($user = $token->getUser())) { 38 | return null; 39 | } 40 | 41 | return $user; 42 | } 43 | 44 | /** 45 | * Encodes the raw password. 46 | * 47 | * @param UserInterface $user A UserInterface instance 48 | * @param string $password The password to encode 49 | * 50 | * @return string The encoded password 51 | * 52 | * @throws \RuntimeException when no password encoder could be found for the user 53 | */ 54 | public function encodePassword(UserInterface $user, $password) 55 | { 56 | return $this['security.encoder_factory']->getEncoder($user)->encodePassword($password, $user->getSalt()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Silex/Application/SwiftmailerTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Application; 13 | 14 | /** 15 | * Swiftmailer trait. 16 | * 17 | * @author Fabien Potencier 18 | */ 19 | trait SwiftmailerTrait 20 | { 21 | /** 22 | * Sends an email. 23 | * 24 | * @param \Swift_Message $message A \Swift_Message instance 25 | */ 26 | public function mail(\Swift_Message $message) 27 | { 28 | return $this['mailer']->send($message); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Silex/Application/TranslationTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Application; 13 | 14 | /** 15 | * Translation trait. 16 | * 17 | * @author Fabien Potencier 18 | */ 19 | trait TranslationTrait 20 | { 21 | /** 22 | * Translates the given message. 23 | * 24 | * @param string $id The message id 25 | * @param array $parameters An array of parameters for the message 26 | * @param string $domain The domain for the message 27 | * @param string $locale The locale 28 | * 29 | * @return string The translated string 30 | */ 31 | public function trans($id, array $parameters = array(), $domain = 'messages', $locale = null) 32 | { 33 | return $this['translator']->trans($id, $parameters, $domain, $locale); 34 | } 35 | 36 | /** 37 | * Translates the given choice message by choosing a translation according to a number. 38 | * 39 | * @param string $id The message id 40 | * @param integer $number The number to use to find the indice of the message 41 | * @param array $parameters An array of parameters for the message 42 | * @param string $domain The domain for the message 43 | * @param string $locale The locale 44 | * 45 | * @return string The translated string 46 | */ 47 | public function transChoice($id, $number, array $parameters = array(), $domain = 'messages', $locale = null) 48 | { 49 | return $this['translator']->transChoice($id, $number, $parameters, $domain, $locale); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Silex/Application/TwigTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Application; 13 | 14 | use Symfony\Component\HttpFoundation\Response; 15 | use Symfony\Component\HttpFoundation\StreamedResponse; 16 | 17 | /** 18 | * Twig trait. 19 | * 20 | * @author Fabien Potencier 21 | */ 22 | trait TwigTrait 23 | { 24 | /** 25 | * Renders a view and returns a Response. 26 | * 27 | * To stream a view, pass an instance of StreamedResponse as a third argument. 28 | * 29 | * @param string $view The view name 30 | * @param array $parameters An array of parameters to pass to the view 31 | * @param Response $response A Response instance 32 | * 33 | * @return Response A Response instance 34 | */ 35 | public function render($view, array $parameters = array(), Response $response = null) 36 | { 37 | if (null === $response) { 38 | $response = new Response(); 39 | } 40 | 41 | $twig = $this['twig']; 42 | 43 | if ($response instanceof StreamedResponse) { 44 | $response->setCallback(function () use ($twig, $view, $parameters) { 45 | $twig->display($view, $parameters); 46 | }); 47 | } else { 48 | $response->setContent($twig->render($view, $parameters)); 49 | } 50 | 51 | return $response; 52 | } 53 | 54 | /** 55 | * Renders a view. 56 | * 57 | * @param string $view The view name 58 | * @param array $parameters An array of parameters to pass to the view 59 | * 60 | * @return Response A Response instance 61 | */ 62 | public function renderView($view, array $parameters = array()) 63 | { 64 | return $this['twig']->render($view, $parameters); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Silex/Application/UrlGeneratorTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Application; 13 | 14 | /** 15 | * UrlGenerator trait. 16 | * 17 | * @author Fabien Potencier 18 | */ 19 | trait UrlGeneratorTrait 20 | { 21 | /** 22 | * Generates a path from the given parameters. 23 | * 24 | * @param string $route The name of the route 25 | * @param mixed $parameters An array of parameters 26 | * 27 | * @return string The generated path 28 | */ 29 | public function path($route, $parameters = array()) 30 | { 31 | return $this['url_generator']->generate($route, $parameters, false); 32 | } 33 | 34 | /** 35 | * Generates an absolute URL from the given parameters. 36 | * 37 | * @param string $route The name of the route 38 | * @param mixed $parameters An array of parameters 39 | * 40 | * @return string The generated URL 41 | */ 42 | public function url($route, $parameters = array()) 43 | { 44 | return $this['url_generator']->generate($route, $parameters, true); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Silex/Controller.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex; 13 | 14 | use Silex\Exception\ControllerFrozenException; 15 | 16 | /** 17 | * A wrapper for a controller, mapped to a route. 18 | * 19 | * __call() forwards method-calls to Route, but returns instance of Controller 20 | * listing Route's methods below, so that IDEs know they are valid 21 | * 22 | * @method \Silex\Controller assert(string $variable, string $regexp) 23 | * @method \Silex\Controller value(string $variable, mixed $default) 24 | * @method \Silex\Controller convert(string $variable, mixed $callback) 25 | * @method \Silex\Controller method(string $method) 26 | * @method \Silex\Controller requireHttp() 27 | * @method \Silex\Controller requireHttps() 28 | * @method \Silex\Controller before(mixed $callback) 29 | * @method \Silex\Controller after(mixed $callback) 30 | * @author Igor Wiedler 31 | */ 32 | class Controller 33 | { 34 | private $route; 35 | private $routeName; 36 | private $isFrozen = false; 37 | 38 | /** 39 | * Constructor. 40 | * 41 | * @param Route $route 42 | */ 43 | public function __construct(Route $route) 44 | { 45 | $this->route = $route; 46 | } 47 | 48 | /** 49 | * Gets the controller's route. 50 | * 51 | * @return Route 52 | */ 53 | public function getRoute() 54 | { 55 | return $this->route; 56 | } 57 | 58 | /** 59 | * Gets the controller's route name. 60 | * 61 | * @return string 62 | */ 63 | public function getRouteName() 64 | { 65 | return $this->routeName; 66 | } 67 | 68 | /** 69 | * Sets the controller's route. 70 | * 71 | * @param string $routeName 72 | * 73 | * @return Controller $this The current Controller instance 74 | */ 75 | public function bind($routeName) 76 | { 77 | if ($this->isFrozen) { 78 | throw new ControllerFrozenException(sprintf('Calling %s on frozen %s instance.', __METHOD__, __CLASS__)); 79 | } 80 | 81 | $this->routeName = $routeName; 82 | 83 | return $this; 84 | } 85 | 86 | public function __call($method, $arguments) 87 | { 88 | if (!method_exists($this->route, $method)) { 89 | throw new \BadMethodCallException(sprintf('Method "%s::%s" does not exist.', get_class($this->route), $method)); 90 | } 91 | 92 | call_user_func_array(array($this->route, $method), $arguments); 93 | 94 | return $this; 95 | } 96 | 97 | /** 98 | * Freezes the controller. 99 | * 100 | * Once the controller is frozen, you can no longer change the route name 101 | */ 102 | public function freeze() 103 | { 104 | $this->isFrozen = true; 105 | } 106 | 107 | public function generateRouteName($prefix) 108 | { 109 | $requirements = $this->route->getRequirements(); 110 | $method = isset($requirements['_method']) ? $requirements['_method'] : ''; 111 | 112 | $routeName = $prefix.$method.$this->route->getPattern(); 113 | $routeName = str_replace(array('/', ':', '|', '-'), '_', $routeName); 114 | $routeName = preg_replace('/[^a-z0-9A-Z_.]+/', '', $routeName); 115 | 116 | return $routeName; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Silex/ControllerCollection.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex; 13 | 14 | use Symfony\Component\Routing\RouteCollection; 15 | use Silex\Controller; 16 | 17 | /** 18 | * Builds Silex controllers. 19 | * 20 | * It acts as a staging area for routes. You are able to set the route name 21 | * until flush() is called, at which point all controllers are frozen and 22 | * converted to a RouteCollection. 23 | * 24 | * @author Igor Wiedler 25 | * @author Fabien Potencier 26 | */ 27 | class ControllerCollection 28 | { 29 | protected $controllers = array(); 30 | protected $defaultRoute; 31 | 32 | /** 33 | * Constructor. 34 | */ 35 | public function __construct(Route $defaultRoute) 36 | { 37 | $this->defaultRoute = $defaultRoute; 38 | } 39 | 40 | /** 41 | * Maps a pattern to a callable. 42 | * 43 | * You can optionally specify HTTP methods that should be matched. 44 | * 45 | * @param string $pattern Matched route pattern 46 | * @param mixed $to Callback that returns the response when matched 47 | * 48 | * @return Controller 49 | */ 50 | public function match($pattern, $to) 51 | { 52 | $route = clone $this->defaultRoute; 53 | $route->setPattern($pattern); 54 | $route->setDefault('_controller', $to); 55 | 56 | $this->controllers[] = $controller = new Controller($route); 57 | 58 | return $controller; 59 | } 60 | 61 | /** 62 | * Maps a GET request to a callable. 63 | * 64 | * @param string $pattern Matched route pattern 65 | * @param mixed $to Callback that returns the response when matched 66 | * 67 | * @return Controller 68 | */ 69 | public function get($pattern, $to) 70 | { 71 | return $this->match($pattern, $to)->method('GET'); 72 | } 73 | 74 | /** 75 | * Maps a POST request to a callable. 76 | * 77 | * @param string $pattern Matched route pattern 78 | * @param mixed $to Callback that returns the response when matched 79 | * 80 | * @return Controller 81 | */ 82 | public function post($pattern, $to) 83 | { 84 | return $this->match($pattern, $to)->method('POST'); 85 | } 86 | 87 | /** 88 | * Maps a PUT request to a callable. 89 | * 90 | * @param string $pattern Matched route pattern 91 | * @param mixed $to Callback that returns the response when matched 92 | * 93 | * @return Controller 94 | */ 95 | public function put($pattern, $to) 96 | { 97 | return $this->match($pattern, $to)->method('PUT'); 98 | } 99 | 100 | /** 101 | * Maps a DELETE request to a callable. 102 | * 103 | * @param string $pattern Matched route pattern 104 | * @param mixed $to Callback that returns the response when matched 105 | * 106 | * @return Controller 107 | */ 108 | public function delete($pattern, $to) 109 | { 110 | return $this->match($pattern, $to)->method('DELETE'); 111 | } 112 | 113 | public function __call($method, $arguments) 114 | { 115 | if (!method_exists($this->defaultRoute, $method)) { 116 | throw new \BadMethodCallException(sprintf('Method "%s::%s" does not exist.', get_class($this->defaultRoute), $method)); 117 | } 118 | 119 | call_user_func_array(array($this->defaultRoute, $method), $arguments); 120 | 121 | foreach ($this->controllers as $controller) { 122 | call_user_func_array(array($controller, $method), $arguments); 123 | } 124 | 125 | return $this; 126 | } 127 | 128 | /** 129 | * Persists and freezes staged controllers. 130 | * 131 | * @param string $prefix 132 | * 133 | * @return RouteCollection A RouteCollection instance 134 | */ 135 | public function flush($prefix = '') 136 | { 137 | $routes = new RouteCollection(); 138 | 139 | foreach ($this->controllers as $controller) { 140 | if (!$name = $controller->getRouteName()) { 141 | $name = $controller->generateRouteName($prefix); 142 | while ($routes->get($name)) { 143 | $name .= '_'; 144 | } 145 | $controller->bind($name); 146 | } 147 | $routes->add($name, $controller->getRoute()); 148 | $controller->freeze(); 149 | } 150 | 151 | $this->controllers = array(); 152 | 153 | return $routes; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/Silex/ControllerProviderInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex; 13 | 14 | /** 15 | * Interface for controller providers. 16 | * 17 | * @author Fabien Potencier 18 | */ 19 | interface ControllerProviderInterface 20 | { 21 | /** 22 | * Returns routes to connect to the given application. 23 | * 24 | * @param Application $app An Application instance 25 | * 26 | * @return ControllerCollection A ControllerCollection instance 27 | */ 28 | public function connect(Application $app); 29 | } 30 | -------------------------------------------------------------------------------- /src/Silex/ControllerResolver.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex; 13 | 14 | use Symfony\Component\HttpKernel\Controller\ControllerResolver as BaseControllerResolver; 15 | use Symfony\Component\HttpKernel\Log\LoggerInterface; 16 | use Symfony\Component\HttpFoundation\Request; 17 | 18 | /** 19 | * Adds Application as a valid argument for controllers. 20 | * 21 | * @author Fabien Potencier 22 | */ 23 | class ControllerResolver extends BaseControllerResolver 24 | { 25 | protected $app; 26 | 27 | /** 28 | * Constructor. 29 | * 30 | * @param Application $app An Application instance 31 | * @param LoggerInterface $logger A LoggerInterface instance 32 | */ 33 | public function __construct(Application $app, LoggerInterface $logger = null) 34 | { 35 | $this->app = $app; 36 | 37 | parent::__construct($logger); 38 | } 39 | 40 | protected function doGetArguments(Request $request, $controller, array $parameters) 41 | { 42 | foreach ($parameters as $param) { 43 | if ($param->getClass() && $param->getClass()->isInstance($this->app)) { 44 | $request->attributes->set($param->getName(), $this->app); 45 | 46 | break; 47 | } 48 | } 49 | 50 | return parent::doGetArguments($request, $controller, $parameters); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Silex/EventListener/ConverterListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\EventListener; 13 | 14 | use Symfony\Component\HttpKernel\KernelEvents; 15 | use Symfony\Component\HttpKernel\Event\FilterControllerEvent; 16 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 17 | use Symfony\Component\Routing\RouteCollection; 18 | 19 | /** 20 | * Handles converters. 21 | * 22 | * @author Fabien Potencier 23 | */ 24 | class ConverterListener implements EventSubscriberInterface 25 | { 26 | protected $routes; 27 | 28 | /** 29 | * Constructor. 30 | * 31 | * @param RouteCollection $routes A RouteCollection instance 32 | */ 33 | public function __construct(RouteCollection $routes) 34 | { 35 | $this->routes = $routes; 36 | } 37 | 38 | /** 39 | * Handles converters. 40 | * 41 | * @param FilterControllerEvent $event The event to handle 42 | */ 43 | public function onKernelController(FilterControllerEvent $event) 44 | { 45 | $request = $event->getRequest(); 46 | $route = $this->routes->get($request->attributes->get('_route')); 47 | if ($route && $converters = $route->getOption('_converters')) { 48 | foreach ($converters as $name => $callback) { 49 | $request->attributes->set($name, call_user_func($callback, $request->attributes->get($name), $request)); 50 | } 51 | } 52 | } 53 | 54 | public static function getSubscribedEvents() 55 | { 56 | return array( 57 | KernelEvents::CONTROLLER => 'onKernelController', 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Silex/EventListener/LocaleListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\EventListener; 13 | 14 | use Symfony\Component\HttpKernel\Event\GetResponseEvent; 15 | use Symfony\Component\HttpKernel\EventListener\LocaleListener as BaseLocaleListener; 16 | use Symfony\Component\Routing\RequestContextAwareInterface; 17 | use Silex\Application; 18 | 19 | /** 20 | * Initializes the locale based on the current request. 21 | * 22 | * @author Fabien Potencier 23 | */ 24 | class LocaleListener extends BaseLocaleListener 25 | { 26 | protected $app; 27 | 28 | public function __construct(Application $app, RequestContextAwareInterface $router = null) 29 | { 30 | parent::__construct($app['locale'], $router); 31 | 32 | $this->app = $app; 33 | } 34 | 35 | public function onKernelRequest(GetResponseEvent $event) 36 | { 37 | parent::onKernelRequest($event); 38 | 39 | $this->app['locale'] = $event->getRequest()->getLocale(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Silex/EventListener/MiddlewareListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\EventListener; 13 | 14 | use Symfony\Component\HttpKernel\KernelEvents; 15 | use Symfony\Component\HttpKernel\Event\GetResponseEvent; 16 | use Symfony\Component\HttpKernel\Event\FilterResponseEvent; 17 | use Symfony\Component\HttpFoundation\Response; 18 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 19 | use Silex\Application; 20 | 21 | /** 22 | * Manages the route middlewares. 23 | * 24 | * @author Fabien Potencier 25 | */ 26 | class MiddlewareListener implements EventSubscriberInterface 27 | { 28 | protected $app; 29 | 30 | /** 31 | * Constructor. 32 | * 33 | * @param Application $app An Application instance 34 | */ 35 | public function __construct(Application $app) 36 | { 37 | $this->app = $app; 38 | } 39 | 40 | /** 41 | * Runs before filters. 42 | * 43 | * @param GetResponseEvent $event The event to handle 44 | */ 45 | public function onKernelRequest(GetResponseEvent $event) 46 | { 47 | $request = $event->getRequest(); 48 | $routeName = $request->attributes->get('_route'); 49 | if (!$route = $this->app['routes']->get($routeName)) { 50 | return; 51 | } 52 | 53 | foreach ((array) $route->getOption('_before_middlewares') as $callback) { 54 | $ret = call_user_func($callback, $request, $this->app); 55 | if ($ret instanceof Response) { 56 | $event->setResponse($ret); 57 | 58 | return; 59 | } elseif (null !== $ret) { 60 | throw new \RuntimeException(sprintf('A before middleware for route "%s" returned an invalid response value. Must return null or an instance of Response.', $routeName)); 61 | } 62 | } 63 | } 64 | 65 | /** 66 | * Runs after filters. 67 | * 68 | * @param FilterResponseEvent $event The event to handle 69 | */ 70 | public function onKernelResponse(FilterResponseEvent $event) 71 | { 72 | $request = $event->getRequest(); 73 | $routeName = $request->attributes->get('_route'); 74 | if (!$route = $this->app['routes']->get($routeName)) { 75 | return; 76 | } 77 | 78 | foreach ((array) $route->getOption('_after_middlewares') as $callback) { 79 | $response = call_user_func($callback, $request, $event->getResponse(), $this->app); 80 | if ($response instanceof Response) { 81 | $event->setResponse($response); 82 | } elseif (null !== $response) { 83 | throw new \RuntimeException(sprintf('An after middleware for route "%s" returned an invalid response value. Must return null or an instance of Response.', $routeName)); 84 | } 85 | } 86 | } 87 | 88 | public static function getSubscribedEvents() 89 | { 90 | return array( 91 | // this must be executed after the late events defined with before() (and their priority is 512) 92 | KernelEvents::REQUEST => array('onKernelRequest', -1024), 93 | KernelEvents::RESPONSE => array('onKernelResponse', 128), 94 | ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Silex/EventListener/StringToResponseListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\EventListener; 13 | 14 | use Symfony\Component\HttpKernel\KernelEvents; 15 | use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; 16 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 17 | use Symfony\Component\HttpFoundation\Response; 18 | 19 | /** 20 | * Converts string responses to proper Response instances. 21 | * 22 | * @author Fabien Potencier 23 | */ 24 | class StringToResponseListener implements EventSubscriberInterface 25 | { 26 | /** 27 | * Handles string responses. 28 | * 29 | * @param GetResponseForControllerResultEvent $event The event to handle 30 | */ 31 | public function onKernelView(GetResponseForControllerResultEvent $event) 32 | { 33 | $response = $event->getControllerResult(); 34 | 35 | if (!( 36 | null === $response 37 | || is_array($response) 38 | || $response instanceof Response 39 | || (is_object($response) && !method_exists($response, '__toString')) 40 | )) { 41 | $event->setResponse(new Response((string) $response)); 42 | } 43 | } 44 | 45 | public static function getSubscribedEvents() 46 | { 47 | return array( 48 | KernelEvents::VIEW => array('onKernelView', -10), 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Silex/Exception/ControllerFrozenException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Exception; 13 | 14 | /** 15 | * Exception, is thrown when a frozen controller is modified 16 | * 17 | * @author Igor Wiedler 18 | */ 19 | class ControllerFrozenException extends \RuntimeException 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /src/Silex/ExceptionHandler.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex; 13 | 14 | use Symfony\Component\HttpKernel\Debug\ExceptionHandler as DebugExceptionHandler; 15 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 16 | use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; 17 | use Symfony\Component\HttpKernel\KernelEvents; 18 | 19 | /** 20 | * Defaults exception handler. 21 | * 22 | * @author Fabien Potencier 23 | */ 24 | class ExceptionHandler implements EventSubscriberInterface 25 | { 26 | protected $debug; 27 | protected $enabled; 28 | 29 | public function __construct($debug) 30 | { 31 | $this->debug = $debug; 32 | $this->enabled = true; 33 | } 34 | 35 | public function disable() 36 | { 37 | $this->enabled = false; 38 | } 39 | 40 | public function onSilexError(GetResponseForExceptionEvent $event) 41 | { 42 | if (!$this->enabled) { 43 | return; 44 | } 45 | 46 | $handler = new DebugExceptionHandler($this->debug); 47 | 48 | $event->setResponse($handler->createResponse($event->getException())); 49 | } 50 | 51 | /** 52 | * {@inheritdoc} 53 | */ 54 | public static function getSubscribedEvents() 55 | { 56 | return array(KernelEvents::EXCEPTION => array('onSilexError', -255)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Silex/ExceptionListenerWrapper.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex; 13 | 14 | use Symfony\Component\HttpFoundation\Response; 15 | use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; 16 | use Symfony\Component\HttpKernel\KernelEvents; 17 | use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; 18 | use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; 19 | use Silex\Application; 20 | 21 | /** 22 | * Wraps exception listeners. 23 | * 24 | * @author Fabien Potencier 25 | */ 26 | class ExceptionListenerWrapper 27 | { 28 | protected $app; 29 | protected $callback; 30 | 31 | /** 32 | * Constructor. 33 | * 34 | * @param Application $app An Application instance 35 | */ 36 | public function __construct(Application $app, $callback) 37 | { 38 | $this->app = $app; 39 | $this->callback = $callback; 40 | } 41 | 42 | public function __invoke(GetResponseForExceptionEvent $event) 43 | { 44 | $exception = $event->getException(); 45 | 46 | if (!$this->shouldRun($exception)) { 47 | return; 48 | } 49 | 50 | $code = $exception instanceof HttpExceptionInterface ? $exception->getStatusCode() : 500; 51 | 52 | $response = call_user_func($this->callback, $exception, $code); 53 | 54 | $this->ensureResponse($response, $event); 55 | } 56 | 57 | protected function shouldRun(\Exception $exception) 58 | { 59 | if (is_array($this->callback)) { 60 | $callbackReflection = new \ReflectionMethod($this->callback[0], $this->callback[1]); 61 | } elseif (is_object($this->callback) && !$this->callback instanceof \Closure) { 62 | $callbackReflection = new \ReflectionObject($this->callback); 63 | $callbackReflection = $callbackReflection->getMethod('__invoke'); 64 | } else { 65 | $callbackReflection = new \ReflectionFunction($this->callback); 66 | } 67 | 68 | if ($callbackReflection->getNumberOfParameters() > 0) { 69 | $parameters = $callbackReflection->getParameters(); 70 | $expectedException = $parameters[0]; 71 | if ($expectedException->getClass() && !$expectedException->getClass()->isInstance($exception)) { 72 | return false; 73 | } 74 | } 75 | 76 | return true; 77 | } 78 | 79 | protected function ensureResponse($response, GetResponseForExceptionEvent $event) 80 | { 81 | if ($response instanceof Response) { 82 | $event->setResponse($response); 83 | } else { 84 | $viewEvent = new GetResponseForControllerResultEvent($this->app['kernel'], $event->getRequest(), $event->getRequestType(), $response); 85 | $this->app['dispatcher']->dispatch(KernelEvents::VIEW, $viewEvent); 86 | 87 | if ($viewEvent->hasResponse()) { 88 | $event->setResponse($viewEvent->getResponse()); 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Silex/HttpCache.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex; 13 | 14 | use Symfony\Component\HttpKernel\HttpCache\HttpCache as BaseHttpCache; 15 | use Symfony\Component\HttpFoundation\Request; 16 | 17 | /** 18 | * HTTP Cache extension to allow using the run() shortcut. 19 | * 20 | * @author Fabien Potencier 21 | */ 22 | class HttpCache extends BaseHttpCache 23 | { 24 | /** 25 | * Handles the Request and delivers the Response. 26 | * 27 | * @param Request $request The Request object 28 | */ 29 | public function run(Request $request = null) 30 | { 31 | if (null === $request) { 32 | $request = Request::createFromGlobals(); 33 | } 34 | 35 | $response = $this->handle($request); 36 | $response->send(); 37 | $this->terminate($request, $response); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Silex/LazyUrlMatcher.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex; 13 | 14 | use Symfony\Component\Routing\RequestContext; 15 | use Symfony\Component\Routing\Matcher\UrlMatcherInterface; 16 | 17 | /** 18 | * Implements a lazy UrlMatcher. 19 | * 20 | * @author Igor Wiedler 21 | */ 22 | class LazyUrlMatcher implements UrlMatcherInterface 23 | { 24 | private $factory; 25 | 26 | public function __construct(\Closure $factory) 27 | { 28 | $this->factory = $factory; 29 | } 30 | 31 | /** 32 | * Returns the corresponding UrlMatcherInterface instance. 33 | * 34 | * @return UrlMatcherInterface 35 | */ 36 | public function getUrlMatcher() 37 | { 38 | $urlMatcher = call_user_func($this->factory); 39 | if (!$urlMatcher instanceof UrlMatcherInterface) { 40 | throw new \LogicException("Factory supplied to LazyUrlMatcher must return implementation of UrlMatcherInterface."); 41 | } 42 | 43 | return $urlMatcher; 44 | } 45 | 46 | /** 47 | * {@inheritdoc} 48 | */ 49 | public function match($pathinfo) 50 | { 51 | return $this->getUrlMatcher()->match($pathinfo); 52 | } 53 | 54 | /** 55 | * {@inheritdoc} 56 | */ 57 | public function setContext(RequestContext $context) 58 | { 59 | $this->getUrlMatcher()->setContext($context); 60 | } 61 | 62 | /** 63 | * {@inheritdoc} 64 | */ 65 | public function getContext() 66 | { 67 | return $this->getUrlMatcher()->getContext(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Silex/Provider/DoctrineServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\ServiceProviderInterface; 16 | use Doctrine\DBAL\DriverManager; 17 | use Doctrine\DBAL\Configuration; 18 | use Doctrine\Common\EventManager; 19 | 20 | /** 21 | * Doctrine DBAL Provider. 22 | * 23 | * @author Fabien Potencier 24 | */ 25 | class DoctrineServiceProvider implements ServiceProviderInterface 26 | { 27 | public function register(Application $app) 28 | { 29 | $app['db.default_options'] = array( 30 | 'driver' => 'pdo_mysql', 31 | 'dbname' => null, 32 | 'host' => 'localhost', 33 | 'user' => 'root', 34 | 'password' => null, 35 | ); 36 | 37 | $app['dbs.options.initializer'] = $app->protect(function () use ($app) { 38 | static $initialized = false; 39 | 40 | if ($initialized) { 41 | return; 42 | } 43 | 44 | $initialized = true; 45 | 46 | if (!isset($app['dbs.options'])) { 47 | $app['dbs.options'] = array('default' => isset($app['db.options']) ? $app['db.options'] : array()); 48 | } 49 | 50 | $tmp = $app['dbs.options']; 51 | foreach ($tmp as $name => &$options) { 52 | $options = array_replace($app['db.default_options'], $options); 53 | 54 | if (!isset($app['dbs.default'])) { 55 | $app['dbs.default'] = $name; 56 | } 57 | } 58 | $app['dbs.options'] = $tmp; 59 | }); 60 | 61 | $app['dbs'] = $app->share(function ($app) { 62 | $app['dbs.options.initializer'](); 63 | 64 | $dbs = new \Pimple(); 65 | foreach ($app['dbs.options'] as $name => $options) { 66 | if ($app['dbs.default'] === $name) { 67 | // we use shortcuts here in case the default has been overridden 68 | $config = $app['db.config']; 69 | $manager = $app['db.event_manager']; 70 | } else { 71 | $config = $app['dbs.config'][$name]; 72 | $manager = $app['dbs.event_manager'][$name]; 73 | } 74 | 75 | $dbs[$name] = DriverManager::getConnection($options, $config, $manager); 76 | } 77 | 78 | return $dbs; 79 | }); 80 | 81 | $app['dbs.config'] = $app->share(function ($app) { 82 | $app['dbs.options.initializer'](); 83 | 84 | $configs = new \Pimple(); 85 | foreach ($app['dbs.options'] as $name => $options) { 86 | $configs[$name] = new Configuration(); 87 | } 88 | 89 | return $configs; 90 | }); 91 | 92 | $app['dbs.event_manager'] = $app->share(function ($app) { 93 | $app['dbs.options.initializer'](); 94 | 95 | $managers = new \Pimple(); 96 | foreach ($app['dbs.options'] as $name => $options) { 97 | $managers[$name] = new EventManager(); 98 | } 99 | 100 | return $managers; 101 | }); 102 | 103 | // shortcuts for the "first" DB 104 | $app['db'] = $app->share(function ($app) { 105 | $dbs = $app['dbs']; 106 | 107 | return $dbs[$app['dbs.default']]; 108 | }); 109 | 110 | $app['db.config'] = $app->share(function ($app) { 111 | $dbs = $app['dbs.config']; 112 | 113 | return $dbs[$app['dbs.default']]; 114 | }); 115 | 116 | $app['db.event_manager'] = $app->share(function ($app) { 117 | $dbs = $app['dbs.event_manager']; 118 | 119 | return $dbs[$app['dbs.default']]; 120 | }); 121 | } 122 | 123 | public function boot(Application $app) 124 | { 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Silex/Provider/FormServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\ServiceProviderInterface; 16 | use Symfony\Component\Form\Extension\Csrf\CsrfExtension; 17 | use Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider; 18 | use Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider; 19 | use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension; 20 | use Symfony\Component\Form\Extension\Validator\ValidatorExtension as FormValidatorExtension; 21 | use Symfony\Component\Form\Forms; 22 | 23 | /** 24 | * Symfony Form component Provider. 25 | * 26 | * @author Fabien Potencier 27 | */ 28 | class FormServiceProvider implements ServiceProviderInterface 29 | { 30 | public function register(Application $app) 31 | { 32 | if (!class_exists('Locale') && !class_exists('Symfony\Component\Locale\Stub\StubLocale')) { 33 | throw new \RuntimeException('You must either install the PHP intl extension or the Symfony Locale Component to use the Form extension.'); 34 | } 35 | 36 | if (!class_exists('Locale')) { 37 | $r = new \ReflectionClass('Symfony\Component\Locale\Stub\StubLocale'); 38 | $path = dirname(dirname($r->getFilename())).'/Resources/stubs'; 39 | 40 | require_once $path.'/functions.php'; 41 | require_once $path.'/Collator.php'; 42 | require_once $path.'/IntlDateFormatter.php'; 43 | require_once $path.'/Locale.php'; 44 | require_once $path.'/NumberFormatter.php'; 45 | } 46 | 47 | $app['form.secret'] = md5(__DIR__); 48 | 49 | $app['form.extensions'] = $app->share(function ($app) { 50 | $extensions = array( 51 | new CsrfExtension($app['form.csrf_provider']), 52 | new HttpFoundationExtension(), 53 | ); 54 | 55 | if (isset($app['validator'])) { 56 | $extensions[] = new FormValidatorExtension($app['validator']); 57 | 58 | if (isset($app['translator'])) { 59 | $r = new \ReflectionClass('Symfony\Component\Form\Form'); 60 | $app['translator']->addResource('xliff', dirname($r->getFilename()).'/Resources/translations/validators.'.$app['locale'].'.xlf', $app['locale'], 'validators'); 61 | } 62 | } 63 | 64 | return $extensions; 65 | }); 66 | 67 | $app['form.factory'] = $app->share(function ($app) { 68 | return Forms::createFormFactoryBuilder() 69 | ->addExtensions($app['form.extensions']) 70 | ->getFormFactory() 71 | ; 72 | }); 73 | 74 | $app['form.csrf_provider'] = $app->share(function ($app) { 75 | if (isset($app['session'])) { 76 | return new SessionCsrfProvider($app['session'], $app['form.secret']); 77 | } 78 | 79 | return new DefaultCsrfProvider($app['form.secret']); 80 | }); 81 | } 82 | 83 | public function boot(Application $app) 84 | { 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Silex/Provider/HttpCacheServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\ServiceProviderInterface; 16 | use Silex\HttpCache; 17 | use Symfony\Component\HttpKernel\HttpCache\Esi; 18 | use Symfony\Component\HttpKernel\HttpCache\Store; 19 | 20 | /** 21 | * Symfony HttpKernel component Provider for HTTP cache. 22 | * 23 | * @author Fabien Potencier 24 | */ 25 | class HttpCacheServiceProvider implements ServiceProviderInterface 26 | { 27 | public function register(Application $app) 28 | { 29 | $app['http_cache'] = $app->share(function ($app) { 30 | return new HttpCache($app, $app['http_cache.store'], $app['http_cache.esi'], $app['http_cache.options']); 31 | }); 32 | 33 | $app['http_cache.esi'] = $app->share(function ($app) { 34 | return new Esi(); 35 | }); 36 | 37 | $app['http_cache.store'] = $app->share(function ($app) { 38 | return new Store($app['http_cache.cache_dir']); 39 | }); 40 | 41 | $app['http_cache.options'] = array(); 42 | } 43 | 44 | public function boot(Application $app) 45 | { 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Silex/Provider/MonologServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Provider; 13 | 14 | use Monolog\Logger; 15 | use Monolog\Handler\StreamHandler; 16 | use Silex\Application; 17 | use Silex\ServiceProviderInterface; 18 | use Symfony\Component\HttpFoundation\Request; 19 | use Symfony\Component\HttpFoundation\Response; 20 | use Symfony\Bridge\Monolog\Handler\DebugHandler; 21 | use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; 22 | 23 | /** 24 | * Monolog Provider. 25 | * 26 | * @author Fabien Potencier 27 | */ 28 | class MonologServiceProvider implements ServiceProviderInterface 29 | { 30 | public function register(Application $app) 31 | { 32 | if ($bridge = class_exists('Symfony\Bridge\Monolog\Logger')) { 33 | $app['logger'] = function () use ($app) { 34 | return $app['monolog']; 35 | }; 36 | 37 | $app['monolog.handler.debug'] = function () use ($app) { 38 | return new DebugHandler($app['monolog.level']); 39 | }; 40 | } 41 | 42 | $app['monolog.logger.class'] = $bridge ? 'Symfony\Bridge\Monolog\Logger' : 'Monolog\Logger'; 43 | 44 | $app['monolog'] = $app->share(function ($app) { 45 | $log = new $app['monolog.logger.class']($app['monolog.name']); 46 | 47 | $log->pushHandler($app['monolog.handler']); 48 | 49 | if ($app['debug'] && isset($app['monolog.handler.debug'])) { 50 | $log->pushHandler($app['monolog.handler.debug']); 51 | } 52 | 53 | return $log; 54 | }); 55 | 56 | $app['monolog.handler'] = function () use ($app) { 57 | return new StreamHandler($app['monolog.logfile'], $app['monolog.level']); 58 | }; 59 | 60 | $app['monolog.level'] = function () { 61 | return Logger::DEBUG; 62 | }; 63 | 64 | $app['monolog.name'] = 'myapp'; 65 | } 66 | 67 | public function boot(Application $app) 68 | { 69 | $app->before(function (Request $request) use ($app) { 70 | $app['monolog']->addInfo('> '.$request->getMethod().' '.$request->getRequestUri()); 71 | }); 72 | 73 | $app->error(function (\Exception $e) use ($app) { 74 | $message = sprintf('%s: %s (uncaught exception) at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()); 75 | if ($e instanceof HttpExceptionInterface && $e->getStatusCode() < 500) { 76 | $app['monolog']->addError($message); 77 | } else { 78 | $app['monolog']->addCritical($message); 79 | } 80 | }, 255); 81 | 82 | $app->after(function (Request $request, Response $response) use ($app) { 83 | $app['monolog']->addInfo('< '.$response->getStatusCode()); 84 | }); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Silex/Provider/SerializerServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\ServiceProviderInterface; 16 | use Symfony\Component\Serializer\Serializer; 17 | use Symfony\Component\Serializer\Encoder\JsonEncoder; 18 | use Symfony\Component\Serializer\Encoder\XmlEncoder; 19 | use Symfony\Component\Serializer\Normalizer\CustomNormalizer; 20 | use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; 21 | 22 | /** 23 | * Symfony Serializer component Provider. 24 | * 25 | * @author Fabien Potencier 26 | * @author Marijn Huizendveld 27 | */ 28 | class SerializerServiceProvider implements ServiceProviderInterface 29 | { 30 | /** 31 | * {@inheritDoc} 32 | * 33 | * This method registers a serializer service. {@link http://api.symfony.com/master/Symfony/Component/Serializer/Serializer.html 34 | * The service is provided by the Symfony Serializer component}. 35 | * 36 | * @param Silex\Application $app 37 | */ 38 | public function register(Application $app) 39 | { 40 | $app['serializer'] = $app->share(function () use ($app) { 41 | return new Serializer($app['serializer.normalizers'], $app['serializer.encoders']); 42 | }); 43 | 44 | $app['serializer.encoders'] = $app->share(function () { 45 | return array( 46 | new JsonEncoder(), 47 | new XmlEncoder() 48 | ); 49 | }); 50 | 51 | $app['serializer.normalizers'] = $app->share(function () { 52 | return array( 53 | new CustomNormalizer(), 54 | new GetSetMethodNormalizer() 55 | ); 56 | }); 57 | } 58 | 59 | /** 60 | * {@inheritDoc} 61 | * 62 | * This provider does not execute any code when booting. 63 | * 64 | * @param Silex\Application $app 65 | */ 66 | public function boot(Application $app) 67 | { 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Silex/Provider/ServiceControllerServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\ServiceProviderInterface; 16 | use Silex\ServiceControllerResolver; 17 | 18 | class ServiceControllerServiceProvider implements ServiceProviderInterface 19 | { 20 | public function register(Application $app) 21 | { 22 | $app['resolver'] = $app->share($app->extend('resolver', function ($resolver, $app) { 23 | return new ServiceControllerResolver($resolver, $app); 24 | })); 25 | } 26 | 27 | public function boot(Application $app) 28 | { 29 | // noop 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Silex/Provider/SessionServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\ServiceProviderInterface; 16 | 17 | use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler; 18 | use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; 19 | use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage; 20 | use Symfony\Component\HttpFoundation\Session\Session; 21 | use Symfony\Component\HttpFoundation\Cookie; 22 | use Symfony\Component\HttpKernel\HttpKernelInterface; 23 | use Symfony\Component\HttpKernel\KernelEvents; 24 | use Symfony\Component\HttpKernel\Event\FilterResponseEvent; 25 | use Symfony\Component\HttpKernel\Event\GetResponseEvent; 26 | 27 | /** 28 | * Symfony HttpFoundation component Provider for sessions. 29 | * 30 | * @author Fabien Potencier 31 | */ 32 | class SessionServiceProvider implements ServiceProviderInterface 33 | { 34 | private $app; 35 | 36 | public function register(Application $app) 37 | { 38 | $this->app = $app; 39 | 40 | $app['session.test'] = false; 41 | 42 | $app['session'] = $app->share(function ($app) { 43 | if (!isset($app['session.storage'])) { 44 | if ($app['session.test']) { 45 | $app['session.storage'] = $app['session.storage.test']; 46 | } else { 47 | $app['session.storage'] = $app['session.storage.native']; 48 | } 49 | } 50 | 51 | return new Session($app['session.storage']); 52 | }); 53 | 54 | $app['session.storage.handler'] = $app->share(function ($app) { 55 | return new NativeFileSessionHandler($app['session.storage.save_path']); 56 | }); 57 | 58 | $app['session.storage.native'] = $app->share(function ($app) { 59 | return new NativeSessionStorage( 60 | $app['session.storage.options'], 61 | $app['session.storage.handler'] 62 | ); 63 | }); 64 | 65 | $app['session.storage.test'] = $app->share(function() { 66 | return new MockFileSessionStorage(); 67 | }); 68 | 69 | $app['session.storage.options'] = array(); 70 | $app['session.default_locale'] = 'en'; 71 | $app['session.storage.save_path'] = null; 72 | } 73 | 74 | public function onEarlyKernelRequest(GetResponseEvent $event) 75 | { 76 | $event->getRequest()->setSession($this->app['session']); 77 | } 78 | 79 | public function onKernelRequest(GetResponseEvent $event) 80 | { 81 | if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { 82 | return; 83 | } 84 | 85 | // bootstrap the session 86 | if (!isset($this->app['session'])) { 87 | return; 88 | } 89 | 90 | $session = $this->app['session']; 91 | $cookies = $event->getRequest()->cookies; 92 | 93 | if ($cookies->has($session->getName())) { 94 | $session->setId($cookies->get($session->getName())); 95 | } else { 96 | $session->migrate(false); 97 | } 98 | } 99 | 100 | public function onKernelResponse(FilterResponseEvent $event) 101 | { 102 | if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { 103 | return; 104 | } 105 | 106 | $session = $event->getRequest()->getSession(); 107 | if ($session && $session->isStarted()) { 108 | $session->save(); 109 | 110 | $params = session_get_cookie_params(); 111 | 112 | $event->getResponse()->headers->setCookie(new Cookie($session->getName(), $session->getId(), 0 === $params['lifetime'] ? 0 : time() + $params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly'])); 113 | } 114 | } 115 | 116 | public function boot(Application $app) 117 | { 118 | $app['dispatcher']->addListener(KernelEvents::REQUEST, array($this, 'onEarlyKernelRequest'), 128); 119 | 120 | if ($app['session.test']) { 121 | $app['dispatcher']->addListener(KernelEvents::REQUEST, array($this, 'onKernelRequest'), 192); 122 | $app['dispatcher']->addListener(KernelEvents::RESPONSE, array($this, 'onKernelResponse'), -128); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Silex/Provider/SwiftmailerServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\ServiceProviderInterface; 16 | 17 | /** 18 | * Swiftmailer Provider. 19 | * 20 | * @author Fabien Potencier 21 | */ 22 | class SwiftmailerServiceProvider implements ServiceProviderInterface 23 | { 24 | public function register(Application $app) 25 | { 26 | $app['swiftmailer.options'] = array(); 27 | 28 | $app['mailer.initialized'] = false; 29 | 30 | $app['mailer'] = $app->share(function ($app) { 31 | $app['mailer.initialized'] = true; 32 | 33 | return new \Swift_Mailer($app['swiftmailer.spooltransport']); 34 | }); 35 | 36 | $app['swiftmailer.spooltransport'] = $app->share(function ($app) { 37 | return new \Swift_SpoolTransport($app['swiftmailer.spool']); 38 | }); 39 | 40 | $app['swiftmailer.spool'] = $app->share(function ($app) { 41 | return new \Swift_MemorySpool(); 42 | }); 43 | 44 | $app['swiftmailer.transport'] = $app->share(function ($app) { 45 | $transport = new \Swift_Transport_EsmtpTransport( 46 | $app['swiftmailer.transport.buffer'], 47 | array($app['swiftmailer.transport.authhandler']), 48 | $app['swiftmailer.transport.eventdispatcher'] 49 | ); 50 | 51 | $options = $app['swiftmailer.options'] = array_replace(array( 52 | 'host' => 'localhost', 53 | 'port' => 25, 54 | 'username' => '', 55 | 'password' => '', 56 | 'encryption' => null, 57 | 'auth_mode' => null, 58 | ), $app['swiftmailer.options']); 59 | 60 | $transport->setHost($options['host']); 61 | $transport->setPort($options['port']); 62 | $transport->setEncryption($options['encryption']); 63 | $transport->setUsername($options['username']); 64 | $transport->setPassword($options['password']); 65 | $transport->setAuthMode($options['auth_mode']); 66 | 67 | return $transport; 68 | }); 69 | 70 | $app['swiftmailer.transport.buffer'] = $app->share(function () { 71 | return new \Swift_Transport_StreamBuffer(new \Swift_StreamFilters_StringReplacementFilterFactory()); 72 | }); 73 | 74 | $app['swiftmailer.transport.authhandler'] = $app->share(function () { 75 | return new \Swift_Transport_Esmtp_AuthHandler(array( 76 | new \Swift_Transport_Esmtp_Auth_CramMd5Authenticator(), 77 | new \Swift_Transport_Esmtp_Auth_LoginAuthenticator(), 78 | new \Swift_Transport_Esmtp_Auth_PlainAuthenticator(), 79 | )); 80 | }); 81 | 82 | $app['swiftmailer.transport.eventdispatcher'] = $app->share(function () { 83 | return new \Swift_Events_SimpleEventDispatcher(); 84 | }); 85 | } 86 | 87 | public function boot(Application $app) 88 | { 89 | $app->finish(function () use ($app) { 90 | // To speed things up (by avoiding Swift Mailer initialization), flush 91 | // messages only if our mailer has been created (potentially used) 92 | if ($app['mailer.initialized']) { 93 | $app['swiftmailer.spooltransport']->getSpool()->flushQueue($app['swiftmailer.transport']); 94 | } 95 | }); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Silex/Provider/TranslationServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\ServiceProviderInterface; 16 | 17 | use Symfony\Component\Translation\Translator; 18 | use Symfony\Component\Translation\MessageSelector; 19 | use Symfony\Component\Translation\Loader\ArrayLoader; 20 | use Symfony\Component\Translation\Loader\XliffFileLoader; 21 | 22 | /** 23 | * Symfony Translation component Provider. 24 | * 25 | * @author Fabien Potencier 26 | */ 27 | class TranslationServiceProvider implements ServiceProviderInterface 28 | { 29 | public function register(Application $app) 30 | { 31 | $app['translator'] = $app->share(function ($app) { 32 | $translator = new Translator($app['locale'], $app['translator.message_selector']); 33 | 34 | $translator->setFallbackLocale($app['locale_fallback']); 35 | 36 | $translator->addLoader('array', new ArrayLoader()); 37 | $translator->addLoader('xliff', new XliffFileLoader()); 38 | 39 | foreach ($app['translator.domains'] as $domain => $data) { 40 | foreach ($data as $locale => $messages) { 41 | $translator->addResource('array', $messages, $locale, $domain); 42 | } 43 | } 44 | 45 | return $translator; 46 | }); 47 | 48 | $app['translator.message_selector'] = $app->share(function () { 49 | return new MessageSelector(); 50 | }); 51 | 52 | $app['translator.domains'] = array(); 53 | 54 | $app['locale_fallback'] = 'en'; 55 | } 56 | 57 | public function boot(Application $app) 58 | { 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Silex/Provider/TwigCoreExtension.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Provider; 13 | 14 | use Symfony\Component\HttpFoundation\Request; 15 | use Symfony\Component\HttpKernel\HttpKernelInterface; 16 | 17 | /** 18 | * Twig extension. 19 | * 20 | * @author Fabien Potencier 21 | */ 22 | class TwigCoreExtension extends \Twig_Extension 23 | { 24 | public function getFunctions() 25 | { 26 | return array( 27 | 'render' => new \Twig_Function_Method($this, 'render', array('needs_environment' => true, 'is_safe' => array('html'))), 28 | ); 29 | } 30 | 31 | public function render(\Twig_Environment $twig, $uri) 32 | { 33 | $globals = $twig->getGlobals(); 34 | $request = $globals['app']['request']; 35 | 36 | $subRequest = Request::create($uri, 'get', array(), $request->cookies->all(), array(), $request->server->all()); 37 | if ($request->getSession()) { 38 | $subRequest->setSession($request->getSession()); 39 | } 40 | 41 | $response = $globals['app']->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false); 42 | 43 | if (!$response->isSuccessful()) { 44 | throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $request->getUri(), $response->getStatusCode())); 45 | } 46 | 47 | return $response->getContent(); 48 | } 49 | 50 | public function getName() 51 | { 52 | return 'silex'; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Silex/Provider/TwigServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\ServiceProviderInterface; 16 | 17 | use Symfony\Bridge\Twig\Extension\RoutingExtension; 18 | use Symfony\Bridge\Twig\Extension\TranslationExtension; 19 | use Symfony\Bridge\Twig\Extension\FormExtension; 20 | use Symfony\Bridge\Twig\Extension\SecurityExtension; 21 | use Symfony\Bridge\Twig\Form\TwigRendererEngine; 22 | use Symfony\Bridge\Twig\Form\TwigRenderer; 23 | 24 | /** 25 | * Twig integration for Silex. 26 | * 27 | * @author Fabien Potencier 28 | */ 29 | class TwigServiceProvider implements ServiceProviderInterface 30 | { 31 | public function register(Application $app) 32 | { 33 | $app['twig.options'] = array(); 34 | $app['twig.form.templates'] = array('form_div_layout.html.twig'); 35 | $app['twig.path'] = array(); 36 | $app['twig.templates'] = array(); 37 | 38 | $app['twig'] = $app->share(function ($app) { 39 | $app['twig.options'] = array_replace( 40 | array( 41 | 'charset' => $app['charset'], 42 | 'debug' => $app['debug'], 43 | 'strict_variables' => $app['debug'], 44 | ), $app['twig.options'] 45 | ); 46 | 47 | $twig = new \Twig_Environment($app['twig.loader'], $app['twig.options']); 48 | $twig->addGlobal('app', $app); 49 | $twig->addExtension(new TwigCoreExtension()); 50 | 51 | if ($app['debug']) { 52 | $twig->addExtension(new \Twig_Extension_Debug()); 53 | } 54 | 55 | if (class_exists('Symfony\Bridge\Twig\Extension\RoutingExtension')) { 56 | if (isset($app['url_generator'])) { 57 | $twig->addExtension(new RoutingExtension($app['url_generator'])); 58 | } 59 | 60 | if (isset($app['translator'])) { 61 | $twig->addExtension(new TranslationExtension($app['translator'])); 62 | } 63 | 64 | if (isset($app['security'])) { 65 | $twig->addExtension(new SecurityExtension($app['security'])); 66 | } 67 | 68 | if (isset($app['form.factory'])) { 69 | $app['twig.form.engine'] = $app->share(function ($app) { 70 | return new TwigRendererEngine($app['twig.form.templates']); 71 | }); 72 | 73 | $app['twig.form.renderer'] = $app->share(function ($app) { 74 | return new TwigRenderer($app['twig.form.engine'], $app['form.csrf_provider']); 75 | }); 76 | 77 | $twig->addExtension(new FormExtension($app['twig.form.renderer'])); 78 | 79 | // add loader for Symfony built-in form templates 80 | $reflected = new \ReflectionClass('Symfony\Bridge\Twig\Extension\FormExtension'); 81 | $path = dirname($reflected->getFileName()).'/../Resources/views/Form'; 82 | $app['twig.loader']->addLoader(new \Twig_Loader_Filesystem($path)); 83 | } 84 | } 85 | 86 | return $twig; 87 | }); 88 | 89 | $app['twig.loader.filesystem'] = $app->share(function ($app) { 90 | return new \Twig_Loader_Filesystem($app['twig.path']); 91 | }); 92 | 93 | $app['twig.loader.array'] = $app->share(function ($app) { 94 | return new \Twig_Loader_Array($app['twig.templates']); 95 | }); 96 | 97 | $app['twig.loader'] = $app->share(function ($app) { 98 | return new \Twig_Loader_Chain(array( 99 | $app['twig.loader.filesystem'], 100 | $app['twig.loader.array'], 101 | )); 102 | }); 103 | } 104 | 105 | public function boot(Application $app) 106 | { 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Silex/Provider/UrlGeneratorServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\ServiceProviderInterface; 16 | 17 | use Symfony\Component\Routing\Generator\UrlGenerator; 18 | 19 | /** 20 | * Symfony Routing component Provider for URL generation. 21 | * 22 | * @author Fabien Potencier 23 | */ 24 | class UrlGeneratorServiceProvider implements ServiceProviderInterface 25 | { 26 | public function register(Application $app) 27 | { 28 | $app['url_generator'] = $app->share(function ($app) { 29 | $app->flush(); 30 | 31 | return new UrlGenerator($app['routes'], $app['request_context']); 32 | }); 33 | } 34 | 35 | public function boot(Application $app) 36 | { 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Silex/Provider/ValidatorServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\ServiceProviderInterface; 16 | 17 | use Symfony\Component\Validator\Validator; 18 | use Symfony\Component\Validator\DefaultTranslator; 19 | use Symfony\Component\Validator\Mapping\ClassMetadataFactory; 20 | use Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader; 21 | use Symfony\Component\Validator\ConstraintValidatorFactory; 22 | 23 | /** 24 | * Symfony Validator component Provider. 25 | * 26 | * @author Fabien Potencier 27 | */ 28 | class ValidatorServiceProvider implements ServiceProviderInterface 29 | { 30 | public function register(Application $app) 31 | { 32 | $app['validator'] = $app->share(function ($app) { 33 | $r = new \ReflectionClass('Symfony\Component\Validator\Validator'); 34 | 35 | if (isset($app['translator'])) { 36 | $app['translator']->addResource('xliff', dirname($r->getFilename()).'/Resources/translations/validators.'.$app['locale'].'.xlf', $app['locale'], 'validators'); 37 | } 38 | 39 | $params = $r->getConstructor()->getParameters(); 40 | if ('validatorInitializers' === $params[2]->getName()) { 41 | // BC: to be removed before 1.0 42 | // Compatibility with symfony/validator 2.1 43 | // can be removed once silex requires 2.2 44 | return new Validator( 45 | $app['validator.mapping.class_metadata_factory'], 46 | $app['validator.validator_factory'] 47 | ); 48 | } else { 49 | return new Validator( 50 | $app['validator.mapping.class_metadata_factory'], 51 | $app['validator.validator_factory'], 52 | isset($app['translator']) ? $app['translator'] : new DefaultTranslator() 53 | ); 54 | } 55 | }); 56 | 57 | $app['validator.mapping.class_metadata_factory'] = $app->share(function ($app) { 58 | return new ClassMetadataFactory(new StaticMethodLoader()); 59 | }); 60 | 61 | $app['validator.validator_factory'] = $app->share(function () { 62 | return new ConstraintValidatorFactory(); 63 | }); 64 | } 65 | 66 | public function boot(Application $app) 67 | { 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Silex/RedirectableUrlMatcher.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex; 13 | 14 | use Symfony\Component\HttpFoundation\RedirectResponse; 15 | use Symfony\Component\Routing\Matcher\RedirectableUrlMatcher as BaseRedirectableUrlMatcher; 16 | use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface; 17 | 18 | /** 19 | * Implements the RedirectableUrlMatcherInterface for Silex. 20 | * 21 | * @author Fabien Potencier 22 | */ 23 | class RedirectableUrlMatcher extends BaseRedirectableUrlMatcher 24 | { 25 | /** 26 | * @see RedirectableUrlMatcherInterface::match() 27 | */ 28 | public function redirect($path, $route, $scheme = null) 29 | { 30 | $url = $this->context->getBaseUrl().$path; 31 | 32 | if ($this->context->getHost()) { 33 | if ($scheme) { 34 | $port = ''; 35 | if ('http' === $scheme && 80 != $this->context->getHttpPort()) { 36 | $port = ':'.$this->context->getHttpPort(); 37 | } elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) { 38 | $port = ':'.$this->context->getHttpsPort(); 39 | } 40 | 41 | $url = $scheme.'://'.$this->context->getHost().$port.$url; 42 | } 43 | } 44 | 45 | return array( 46 | '_controller' => function ($url) { return new RedirectResponse($url, 301); }, 47 | 'url' => $url, 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Silex/Route.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex; 13 | 14 | use Symfony\Component\Routing\Route as BaseRoute; 15 | 16 | /** 17 | * A wrapper for a controller, mapped to a route. 18 | * 19 | * @author Fabien Potencier 20 | */ 21 | class Route extends BaseRoute 22 | { 23 | public function __construct($pattern = '', array $defaults = array(), array $requirements = array(), array $options = array()) 24 | { 25 | parent::__construct($pattern, $defaults, $requirements, $options); 26 | } 27 | 28 | /** 29 | * Sets the requirement for a route variable. 30 | * 31 | * @param string $variable The variable name 32 | * @param string $regexp The regexp to apply 33 | * 34 | * @return Route $this The current route instance 35 | */ 36 | public function assert($variable, $regexp) 37 | { 38 | $this->setRequirement($variable, $regexp); 39 | 40 | return $this; 41 | } 42 | 43 | /** 44 | * Sets the default value for a route variable. 45 | * 46 | * @param string $variable The variable name 47 | * @param mixed $default The default value 48 | * 49 | * @return Route $this The current Route instance 50 | */ 51 | public function value($variable, $default) 52 | { 53 | $this->setDefault($variable, $default); 54 | 55 | return $this; 56 | } 57 | 58 | /** 59 | * Sets a converter for a route variable. 60 | * 61 | * @param string $variable The variable name 62 | * @param mixed $callback A PHP callback that converts the original value 63 | * 64 | * @return Route $this The current Route instance 65 | */ 66 | public function convert($variable, $callback) 67 | { 68 | $converters = $this->getOption('_converters'); 69 | $converters[$variable] = $callback; 70 | $this->setOption('_converters', $converters); 71 | 72 | return $this; 73 | } 74 | 75 | /** 76 | * Sets the requirement for the HTTP method. 77 | * 78 | * @param string $method The HTTP method name. Multiple methods can be supplied, delimited by a pipe character '|', eg. 'GET|POST' 79 | * 80 | * @return Route $this The current Route instance 81 | */ 82 | public function method($method) 83 | { 84 | $this->setRequirement('_method', $method); 85 | 86 | return $this; 87 | } 88 | 89 | /** 90 | * Sets the requirement of HTTP (no HTTPS) on this Route. 91 | * 92 | * @return Route $this The current Route instance 93 | */ 94 | public function requireHttp() 95 | { 96 | $this->setRequirement('_scheme', 'http'); 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * Sets the requirement of HTTPS on this Route. 103 | * 104 | * @return Route $this The current Route instance 105 | */ 106 | public function requireHttps() 107 | { 108 | $this->setRequirement('_scheme', 'https'); 109 | 110 | return $this; 111 | } 112 | 113 | /** 114 | * Sets a callback to handle before triggering the route callback. 115 | * 116 | * @param mixed $callback A PHP callback to be triggered when the Route is matched, just before the route callback 117 | * 118 | * @return Route $this The current Route instance 119 | */ 120 | public function before($callback) 121 | { 122 | $callbacks = $this->getOption('_before_middlewares'); 123 | $callbacks[] = $callback; 124 | $this->setOption('_before_middlewares', $callbacks); 125 | 126 | return $this; 127 | } 128 | 129 | /** 130 | * Sets a callback to handle after the route callback. 131 | * 132 | * @param mixed $callback A PHP callback to be triggered after the route callback 133 | * 134 | * @return Route $this The current Route instance 135 | */ 136 | public function after($callback) 137 | { 138 | $callbacks = $this->getOption('_after_middlewares'); 139 | $callbacks[] = $callback; 140 | $this->setOption('_after_middlewares', $callbacks); 141 | 142 | return $this; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/Silex/Route/SecurityTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Route; 13 | 14 | use Symfony\Component\Security\Core\Exception\AccessDeniedException; 15 | 16 | /** 17 | * Security trait. 18 | * 19 | * @author Fabien Potencier 20 | */ 21 | trait SecurityTrait 22 | { 23 | public function secure($roles) 24 | { 25 | $this->before(function ($request, $app) use ($roles) { 26 | if (!$app['security']->isGranted($roles)) { 27 | throw new AccessDeniedException(); 28 | } 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Silex/ServiceControllerResolver.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex; 13 | 14 | use Silex\Application; 15 | use Symfony\Component\HttpFoundation\Request; 16 | use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; 17 | 18 | /** 19 | * Enables name_of_service:method_name syntax for declaring controllers. 20 | * 21 | * @link http://silex.sensiolabs.org/doc/cookbook/controllers_as_services.html 22 | */ 23 | class ServiceControllerResolver implements ControllerResolverInterface 24 | { 25 | const SERVICE_PATTERN = "/[A-Za-z0-9\._\-]+:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/"; 26 | 27 | protected $resolver; 28 | protected $app; 29 | 30 | /** 31 | * Constructor. 32 | * 33 | * @param ControllerResolverInterface $resolver A ControllerResolverInterface instance to delegate to 34 | * @param Application $app An Application instance 35 | */ 36 | public function __construct(ControllerResolverInterface $resolver, Application $app) 37 | { 38 | $this->resolver = $resolver; 39 | $this->app = $app; 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function getController(Request $request) 46 | { 47 | $controller = $request->attributes->get('_controller', null); 48 | 49 | if (!is_string($controller) || !preg_match(static::SERVICE_PATTERN, $controller)) { 50 | return $this->resolver->getController($request); 51 | } 52 | 53 | list($service, $method) = explode(':', $controller, 2); 54 | 55 | if (!isset($this->app[$service])) { 56 | throw new \InvalidArgumentException(sprintf('Service "%s" does not exist.', $service)); 57 | } 58 | 59 | return array($this->app[$service], $method); 60 | } 61 | 62 | /** 63 | * {@inheritdoc} 64 | */ 65 | public function getArguments(Request $request, $controller) 66 | { 67 | return $this->resolver->getArguments($request, $controller); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Silex/ServiceProviderInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex; 13 | 14 | /** 15 | * Interface that must implement all Silex service providers. 16 | * 17 | * @author Fabien Potencier 18 | */ 19 | interface ServiceProviderInterface 20 | { 21 | /** 22 | * Registers services on the given app. 23 | * 24 | * This method should only be used to configure services and parameters. 25 | * It should not get services. 26 | * 27 | * @param Application $app An Application instance 28 | */ 29 | public function register(Application $app); 30 | 31 | /** 32 | * Bootstraps the application. 33 | * 34 | * This method is called after all services are registers 35 | * and should be used for "dynamic" configuration (whenever 36 | * a service must be requested). 37 | */ 38 | public function boot(Application $app); 39 | } 40 | -------------------------------------------------------------------------------- /src/Silex/WebTestCase.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex; 13 | 14 | use Symfony\Component\HttpKernel\Client; 15 | use Symfony\Component\HttpKernel\HttpKernel; 16 | 17 | /** 18 | * WebTestCase is the base class for functional tests. 19 | * 20 | * @author Igor Wiedler 21 | */ 22 | abstract class WebTestCase extends \PHPUnit_Framework_TestCase 23 | { 24 | protected $app; 25 | 26 | /** 27 | * PHPUnit setUp for setting up the application. 28 | * 29 | * Note: Child classes that define a setUp method must call 30 | * parent::setUp(). 31 | */ 32 | public function setUp() 33 | { 34 | $this->app = $this->createApplication(); 35 | } 36 | 37 | /** 38 | * Creates the application. 39 | * 40 | * @return HttpKernel 41 | */ 42 | abstract public function createApplication(); 43 | 44 | /** 45 | * Creates a Client. 46 | * 47 | * @param array $server An array of server parameters 48 | * 49 | * @return Client A Client instance 50 | */ 51 | public function createClient(array $server = array()) 52 | { 53 | return new Client($this->app, $server); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Application/FormApplication.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Application; 13 | 14 | use Silex\Application; 15 | 16 | class FormApplication extends Application 17 | { 18 | use Application\FormTrait; 19 | } 20 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Application/FormTraitTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Application; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\FormServiceProvider; 16 | 17 | /** 18 | * FormTrait test cases. 19 | * 20 | * @author Fabien Potencier 21 | */ 22 | class FormTraitTest extends \PHPUnit_Framework_TestCase 23 | { 24 | public function setUp() 25 | { 26 | if (version_compare(phpversion(), '5.4.0', '<')) { 27 | $this->markTestSkipped('PHP 5.4 is required for this test'); 28 | } 29 | 30 | if (!is_dir(__DIR__.'/../../../../vendor/symfony/form')) { 31 | $this->markTestSkipped('Form dependency was not installed.'); 32 | } 33 | } 34 | 35 | public function testForm() 36 | { 37 | $this->assertInstanceOf('Symfony\Component\Form\FormBuilder', $this->createApplication()->form()); 38 | } 39 | 40 | public function createApplication() 41 | { 42 | $app = new FormApplication(); 43 | $app->register(new FormServiceProvider()); 44 | 45 | return $app; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Application/MonologApplication.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Application; 13 | 14 | use Silex\Application; 15 | 16 | class MonologApplication extends Application 17 | { 18 | use Application\MonologTrait; 19 | } 20 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Application/MonologTraitTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Application; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\MonologServiceProvider; 16 | use Monolog\Handler\TestHandler; 17 | use Monolog\Logger; 18 | 19 | /** 20 | * MonologTrait test cases. 21 | * 22 | * @author Fabien Potencier 23 | */ 24 | class MonologTraitTest extends \PHPUnit_Framework_TestCase 25 | { 26 | public function setUp() 27 | { 28 | if (version_compare(phpversion(), '5.4.0', '<')) { 29 | $this->markTestSkipped('PHP 5.4 is required for this test'); 30 | } 31 | 32 | if (!is_dir(__DIR__.'/../../../../vendor/monolog/monolog/src')) { 33 | $this->markTestSkipped('Monolog dependency was not installed.'); 34 | } 35 | } 36 | 37 | public function testLog() 38 | { 39 | $app = $this->createApplication(); 40 | 41 | $app->log('Foo'); 42 | $app->log('Bar', array(), Logger::DEBUG); 43 | $this->assertTrue($app['monolog.handler']->hasInfo('Foo')); 44 | $this->assertTrue($app['monolog.handler']->hasDebug('Bar')); 45 | } 46 | 47 | public function createApplication() 48 | { 49 | $app = new MonologApplication(); 50 | $app->register(new MonologServiceProvider(), array( 51 | 'monolog.handler' => $app->share(function () use ($app) { 52 | return new TestHandler($app['monolog.level']); 53 | }), 54 | )); 55 | 56 | return $app; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Application/SecurityApplication.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Application; 13 | 14 | use Silex\Application; 15 | 16 | class SecurityApplication extends Application 17 | { 18 | use Application\SecurityTrait; 19 | } 20 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Application/SecurityTraitTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Application; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\SecurityServiceProvider; 16 | use Symfony\Component\Security\Core\User\User; 17 | use Symfony\Component\HttpFoundation\Request; 18 | 19 | /** 20 | * SecurityTrait test cases. 21 | * 22 | * @author Fabien Potencier 23 | */ 24 | class SecurityTraitTest extends \PHPUnit_Framework_TestCase 25 | { 26 | public function setUp() 27 | { 28 | if (version_compare(phpversion(), '5.4.0', '<')) { 29 | $this->markTestSkipped('PHP 5.4 is required for this test'); 30 | } 31 | 32 | if (!is_dir(__DIR__.'/../../../../vendor/symfony/security')) { 33 | $this->markTestSkipped('Security dependency was not installed.'); 34 | } 35 | } 36 | 37 | public function testUser() 38 | { 39 | $request = Request::create('/'); 40 | 41 | $app = $this->createApplication(); 42 | $app->get('/', function () { return 'foo'; }); 43 | $app->handle($request); 44 | $this->assertNull($app->user()); 45 | 46 | $request->headers->set('PHP_AUTH_USER', 'fabien'); 47 | $request->headers->set('PHP_AUTH_PW', 'foo'); 48 | $app->handle($request); 49 | $this->assertInstanceOf('Symfony\Component\Security\Core\User\UserInterface', $app->user()); 50 | $this->assertEquals('fabien', $app->user()->getUsername()); 51 | } 52 | 53 | public function testEncodePassword() 54 | { 55 | $app = $this->createApplication(); 56 | 57 | $user = new User('foo', 'bar'); 58 | $this->assertEquals('5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg==', $app->encodePassword($user, 'foo')); 59 | } 60 | 61 | public function createApplication() 62 | { 63 | $app = new SecurityApplication(); 64 | $app->register(new SecurityServiceProvider(), array( 65 | 'security.firewalls' => array( 66 | 'default' => array( 67 | 'http' => true, 68 | 'users' => array( 69 | 'fabien' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), 70 | ), 71 | ), 72 | ), 73 | )); 74 | 75 | return $app; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Application/SwiftmailerApplication.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Application; 13 | 14 | use Silex\Application; 15 | 16 | class SwiftmailerApplication extends Application 17 | { 18 | use Application\SwiftmailerTrait; 19 | } 20 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Application/SwiftmailerTraitTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Application; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\SwiftmailerServiceProvider; 16 | 17 | /** 18 | * SwiftmailerTrait test cases. 19 | * 20 | * @author Fabien Potencier 21 | */ 22 | class SwiftmailerTraitTest extends \PHPUnit_Framework_TestCase 23 | { 24 | public function setUp() 25 | { 26 | if (version_compare(phpversion(), '5.4.0', '<')) { 27 | $this->markTestSkipped('PHP 5.4 is required for this test'); 28 | } 29 | 30 | if (!is_dir(__DIR__.'/../../../../vendor/swiftmailer/swiftmailer')) { 31 | $this->markTestSkipped('Swiftmailer dependency was not installed.'); 32 | } 33 | } 34 | 35 | public function testMail() 36 | { 37 | $app = $this->createApplication(); 38 | 39 | $message = $this->getMockBuilder('Swift_Message')->disableOriginalConstructor()->getMock(); 40 | $app['mailer'] = $mailer = $this->getMockBuilder('Swift_Mailer')->disableOriginalConstructor()->getMock(); 41 | $mailer->expects($this->once()) 42 | ->method('send') 43 | ->with($message) 44 | ; 45 | 46 | $app->mail($message); 47 | } 48 | 49 | public function createApplication() 50 | { 51 | $app = new SwiftmailerApplication(); 52 | $app->register(new SwiftmailerServiceProvider()); 53 | 54 | return $app; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Application/TranslationApplication.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Application; 13 | 14 | use Silex\Application; 15 | 16 | class TranslationApplication extends Application 17 | { 18 | use Application\TranslationTrait; 19 | } 20 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Application/TranslationTraitTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Application; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\TranslationServiceProvider; 16 | 17 | /** 18 | * TranslationTrait test cases. 19 | * 20 | * @author Fabien Potencier 21 | */ 22 | class TranslationTraitTest extends \PHPUnit_Framework_TestCase 23 | { 24 | public function setUp() 25 | { 26 | if (version_compare(phpversion(), '5.4.0', '<')) { 27 | $this->markTestSkipped('PHP 5.4 is required for this test'); 28 | } 29 | 30 | if (!is_dir(__DIR__.'/../../../../vendor/symfony/translation')) { 31 | $this->markTestSkipped('Translation dependency was not installed.'); 32 | } 33 | } 34 | 35 | public function testTrans() 36 | { 37 | $app = $this->createApplication(); 38 | $app['translator'] = $translator = $this->getMockBuilder('Symfony\Component\Translation\Translator')->disableOriginalConstructor()->getMock(); 39 | $translator->expects($this->once())->method('trans'); 40 | $app->trans('foo'); 41 | } 42 | 43 | public function testTransChoice() 44 | { 45 | $app = $this->createApplication(); 46 | $app['translator'] = $translator = $this->getMockBuilder('Symfony\Component\Translation\Translator')->disableOriginalConstructor()->getMock(); 47 | $translator->expects($this->once())->method('transChoice'); 48 | $app->transChoice('foo', 2); 49 | } 50 | 51 | public function createApplication() 52 | { 53 | $app = new TranslationApplication(); 54 | $app->register(new TranslationServiceProvider()); 55 | 56 | return $app; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Application/TwigApplication.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Application; 13 | 14 | use Silex\Application; 15 | 16 | class TwigApplication extends Application 17 | { 18 | use Application\TwigTrait; 19 | } 20 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Application/TwigTraitTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Application; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\TwigServiceProvider; 16 | use Symfony\Component\HttpFoundation\Response; 17 | use Symfony\Component\HttpFoundation\StreamedResponse; 18 | 19 | /** 20 | * TwigTrait test cases. 21 | * 22 | * @author Fabien Potencier 23 | */ 24 | class TwigTraitTest extends \PHPUnit_Framework_TestCase 25 | { 26 | public function setUp() 27 | { 28 | if (version_compare(phpversion(), '5.4.0', '<')) { 29 | $this->markTestSkipped('PHP 5.4 is required for this test'); 30 | } 31 | 32 | if (!is_dir(__DIR__.'/../../../../vendor/twig/twig')) { 33 | $this->markTestSkipped('Twig dependency was not installed.'); 34 | } 35 | } 36 | 37 | public function testRender() 38 | { 39 | $app = $this->createApplication(); 40 | 41 | $app['twig'] = $mailer = $this->getMockBuilder('Twig_Environment')->disableOriginalConstructor()->getMock(); 42 | $mailer->expects($this->once())->method('render')->will($this->returnValue('foo')); 43 | 44 | $response = $app->render('view'); 45 | $this->assertEquals('Symfony\Component\HttpFoundation\Response', get_class($response)); 46 | $this->assertEquals('foo', $response->getContent()); 47 | } 48 | 49 | public function testRenderKeepResponse() 50 | { 51 | $app = $this->createApplication(); 52 | 53 | $app['twig'] = $mailer = $this->getMockBuilder('Twig_Environment')->disableOriginalConstructor()->getMock(); 54 | $mailer->expects($this->once())->method('render')->will($this->returnValue('foo')); 55 | 56 | $response = $app->render('view', array(), new Response('', 404)); 57 | $this->assertEquals(404, $response->getStatusCode()); 58 | } 59 | 60 | public function testRenderForStream() 61 | { 62 | $app = $this->createApplication(); 63 | 64 | $app['twig'] = $mailer = $this->getMockBuilder('Twig_Environment')->disableOriginalConstructor()->getMock(); 65 | $mailer->expects($this->once())->method('display')->will($this->returnCallback(function () { echo 'foo'; })); 66 | 67 | $response = $app->render('view', array(), new StreamedResponse()); 68 | $this->assertEquals('Symfony\Component\HttpFoundation\StreamedResponse', get_class($response)); 69 | 70 | ob_start(); 71 | $response->send(); 72 | $this->assertEquals('foo', ob_get_clean()); 73 | } 74 | 75 | public function testRenderView() 76 | { 77 | $app = $this->createApplication(); 78 | 79 | $app['twig'] = $mailer = $this->getMockBuilder('Twig_Environment')->disableOriginalConstructor()->getMock(); 80 | $mailer->expects($this->once())->method('render'); 81 | 82 | $app->renderView('view'); 83 | } 84 | 85 | public function createApplication() 86 | { 87 | $app = new TwigApplication(); 88 | $app->register(new TwigServiceProvider()); 89 | 90 | return $app; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Application/UrlGeneratorApplication.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Application; 13 | 14 | use Silex\Application; 15 | 16 | class UrlGeneratorApplication extends Application 17 | { 18 | use Application\UrlGeneratorTrait; 19 | } 20 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Application/UrlGeneratorTraitTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Application; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\UrlGeneratorServiceProvider; 16 | 17 | /** 18 | * UrlGeneratorTrait test cases. 19 | * 20 | * @author Fabien Potencier 21 | */ 22 | class UrlGeneratorTraitTest extends \PHPUnit_Framework_TestCase 23 | { 24 | public function setUp() 25 | { 26 | if (version_compare(phpversion(), '5.4.0', '<')) { 27 | $this->markTestSkipped('PHP 5.4 is required for this test'); 28 | } 29 | } 30 | 31 | public function testUrl() 32 | { 33 | $app = $this->createApplication(); 34 | $app['url_generator'] = $translator = $this->getMockBuilder('Symfony\Component\Routing\Generator\UrlGeneratorInterface')->disableOriginalConstructor()->getMock(); 35 | $translator->expects($this->once())->method('generate')->with('foo', array(), true); 36 | $app->url('foo'); 37 | } 38 | 39 | public function testPath() 40 | { 41 | $app = $this->createApplication(); 42 | $app['url_generator'] = $translator = $this->getMockBuilder('Symfony\Component\Routing\Generator\UrlGeneratorInterface')->disableOriginalConstructor()->getMock(); 43 | $translator->expects($this->once())->method('generate')->with('foo', array(), false); 44 | $app->path('foo'); 45 | } 46 | 47 | public function createApplication() 48 | { 49 | $app = new UrlGeneratorApplication(); 50 | $app->register(new UrlGeneratorServiceProvider()); 51 | 52 | return $app; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/Silex/Tests/ControllerResolverTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests; 13 | 14 | use Silex\ControllerResolver; 15 | use Silex\Application; 16 | use Symfony\Component\HttpFoundation\Request; 17 | 18 | /** 19 | * ControllerResolver test cases. 20 | * 21 | * @author Fabien Potencier 22 | */ 23 | class ControllerResolverTest extends \PHPUnit_Framework_TestCase 24 | { 25 | public function testGetArguments() 26 | { 27 | $app = new Application(); 28 | $resolver = new ControllerResolver($app); 29 | 30 | $controller = function (Application $app) {}; 31 | 32 | $args = $resolver->getArguments(Request::create('/'), $controller); 33 | $this->assertSame($app, $args[0]); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Silex/Tests/ControllerTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests; 13 | 14 | use Silex\Controller; 15 | use Silex\Route; 16 | 17 | /** 18 | * Controller test cases. 19 | * 20 | * @author Igor Wiedler 21 | */ 22 | class ControllerTest extends \PHPUnit_Framework_TestCase 23 | { 24 | public function testBind() 25 | { 26 | $controller = new Controller(new Route('/foo')); 27 | $ret = $controller->bind('foo'); 28 | 29 | $this->assertSame($ret, $controller); 30 | $this->assertEquals('foo', $controller->getRouteName()); 31 | } 32 | 33 | /** 34 | * @expectedException Silex\Exception\ControllerFrozenException 35 | */ 36 | public function testBindOnFrozenControllerShouldThrowException() 37 | { 38 | $controller = new Controller(new Route('/foo')); 39 | $controller->bind('foo'); 40 | $controller->freeze(); 41 | $controller->bind('bar'); 42 | } 43 | 44 | public function testAssert() 45 | { 46 | $controller = new Controller(new Route('/foo/{bar}')); 47 | $ret = $controller->assert('bar', '\d+'); 48 | 49 | $this->assertSame($ret, $controller); 50 | $this->assertEquals(array('bar' => '\d+'), $controller->getRoute()->getRequirements()); 51 | } 52 | 53 | public function testValue() 54 | { 55 | $controller = new Controller(new Route('/foo/{bar}')); 56 | $ret = $controller->value('bar', 'foo'); 57 | 58 | $this->assertSame($ret, $controller); 59 | $this->assertEquals(array('bar' => 'foo'), $controller->getRoute()->getDefaults()); 60 | } 61 | 62 | public function testConvert() 63 | { 64 | $controller = new Controller(new Route('/foo/{bar}')); 65 | $ret = $controller->convert('bar', $func = function ($bar) { return $bar; }); 66 | 67 | $this->assertSame($ret, $controller); 68 | $this->assertEquals(array('bar' => $func), $controller->getRoute()->getOption('_converters')); 69 | } 70 | 71 | /** 72 | * @dataProvider provideRouteAndExpectedRouteName 73 | */ 74 | public function testDefaultRouteNameGeneration(Route $route, $expectedRouteName) 75 | { 76 | $controller = new Controller($route); 77 | $controller->bind($controller->generateRouteName('')); 78 | 79 | $this->assertEquals($expectedRouteName, $controller->getRouteName()); 80 | } 81 | 82 | public function provideRouteAndExpectedRouteName() 83 | { 84 | return array( 85 | array(new Route('/Invalid%Symbols#Stripped', array(), array('_method' => 'POST')), 'POST_InvalidSymbolsStripped'), 86 | array(new Route('/post/{id}', array(), array('_method' => 'GET')), 'GET_post_id'), 87 | array(new Route('/colon:pipe|dashes-escaped'), '_colon_pipe_dashes_escaped'), 88 | array(new Route('/underscores_and.periods'), '_underscores_and.periods'), 89 | ); 90 | } 91 | 92 | public function testRouteExtension() 93 | { 94 | $route = new MyRoute(); 95 | 96 | $controller = new Controller($route); 97 | $controller->foo('foo'); 98 | 99 | $this->assertEquals('foo', $route->foo); 100 | } 101 | 102 | /** 103 | * @expectedException \BadMethodCallException 104 | */ 105 | public function testRouteMethodDoesNotExist() 106 | { 107 | $route = new MyRoute(); 108 | 109 | $controller = new Controller($route); 110 | $controller->bar(); 111 | } 112 | } 113 | 114 | class MyRoute extends Route 115 | { 116 | public $foo; 117 | 118 | public function foo($value) 119 | { 120 | $this->foo = $value; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /tests/Silex/Tests/FunctionalTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests; 13 | 14 | use Silex\Application; 15 | use Silex\Route; 16 | use Silex\ControllerCollection; 17 | use Symfony\Component\HttpFoundation\Request; 18 | use Symfony\Component\HttpFoundation\Response; 19 | 20 | /** 21 | * Functional test cases. 22 | * 23 | * @author Igor Wiedler 24 | */ 25 | class FunctionalTest extends \PHPUnit_Framework_TestCase 26 | { 27 | public function testBind() 28 | { 29 | $app = new Application(); 30 | 31 | $app->get('/', function () { 32 | return 'hello'; 33 | }) 34 | ->bind('homepage'); 35 | 36 | $app->get('/foo', function () { 37 | return 'foo'; 38 | }) 39 | ->bind('foo_abc'); 40 | 41 | $app->flush(); 42 | $routes = $app['routes']; 43 | $this->assertInstanceOf('Symfony\Component\Routing\Route', $routes->get('homepage')); 44 | $this->assertInstanceOf('Symfony\Component\Routing\Route', $routes->get('foo_abc')); 45 | } 46 | 47 | public function testMount() 48 | { 49 | $mounted = new ControllerCollection(new Route()); 50 | $mounted->get('/{name}', function ($name) { return new Response($name); }); 51 | 52 | $app = new Application(); 53 | $app->mount('/hello', $mounted); 54 | 55 | $response = $app->handle(Request::create('/hello/Silex')); 56 | $this->assertEquals('Silex', $response->getContent()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/Silex/Tests/JsonTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests; 13 | 14 | use Silex\Application; 15 | 16 | /** 17 | * JSON test cases. 18 | * 19 | * @author Igor Wiedler 20 | */ 21 | class JsonTest extends \PHPUnit_Framework_TestCase 22 | { 23 | public function testJsonReturnsJsonResponse() 24 | { 25 | $app = new Application(); 26 | 27 | $response = $app->json(); 28 | $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); 29 | $response = json_decode($response->getContent(), true); 30 | $this->assertSame(array(), $response); 31 | } 32 | 33 | public function testJsonUsesData() 34 | { 35 | $app = new Application(); 36 | 37 | $response = $app->json(array('foo' => 'bar')); 38 | $this->assertSame('{"foo":"bar"}', $response->getContent()); 39 | } 40 | 41 | public function testJsonUsesStatus() 42 | { 43 | $app = new Application(); 44 | 45 | $response = $app->json(array(), 202); 46 | $this->assertSame(202, $response->getStatusCode()); 47 | } 48 | 49 | public function testJsonUsesHeaders() 50 | { 51 | $app = new Application(); 52 | 53 | $response = $app->json(array(), 200, array('ETag' => 'foo')); 54 | $this->assertSame('foo', $response->headers->get('ETag')); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Silex/Tests/LazyUrlMatcherTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests; 13 | 14 | use Silex\LazyUrlMatcher; 15 | 16 | /** 17 | * LazyUrlMatcher test case. 18 | * 19 | * @author Leszek Prabucki 20 | */ 21 | class LazyUrlMatcherTest extends \PHPUnit_Framework_TestCase 22 | { 23 | /** 24 | * @covers Silex\LazyUrlMatcher::getUrlMatcher 25 | */ 26 | public function testUserMatcherIsCreatedLazily() 27 | { 28 | $callCounter = 0; 29 | $urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface'); 30 | 31 | $matcher = new LazyUrlMatcher(function () use ($urlMatcher, &$callCounter) { 32 | $callCounter++; 33 | 34 | return $urlMatcher; 35 | }); 36 | 37 | $this->assertEquals(0, $callCounter); 38 | $matcher->match('path'); 39 | $this->assertEquals(1, $callCounter); 40 | } 41 | 42 | /** 43 | * @expectedException LogicException 44 | * @expectedExceptionMessage Factory supplied to LazyUrlMatcher must return implementation of UrlMatcherInterface. 45 | */ 46 | public function testThatCanInjectUrlMatcherOnly() 47 | { 48 | $matcher = new LazyUrlMatcher(function () { 49 | return 'someMatcher'; 50 | }); 51 | 52 | $matcher->match('path'); 53 | } 54 | 55 | /** 56 | * @covers Silex\LazyUrlMatcher::match 57 | */ 58 | public function testMatchIsProxy() 59 | { 60 | $urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface'); 61 | $urlMatcher->expects($this->once()) 62 | ->method('match') 63 | ->with('path') 64 | ->will($this->returnValue('matcherReturnValue')); 65 | 66 | $matcher = new LazyUrlMatcher(function () use ($urlMatcher) { 67 | return $urlMatcher; 68 | }); 69 | $result = $matcher->match('path'); 70 | 71 | $this->assertEquals('matcherReturnValue', $result); 72 | } 73 | 74 | /** 75 | * @covers Silex\LazyUrlMatcher::setContext 76 | */ 77 | public function testSetContextIsProxy() 78 | { 79 | $context = $this->getMock('Symfony\Component\Routing\RequestContext'); 80 | $urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface'); 81 | $urlMatcher->expects($this->once()) 82 | ->method('setContext') 83 | ->with($context); 84 | 85 | $matcher = new LazyUrlMatcher(function () use ($urlMatcher) { 86 | return $urlMatcher; 87 | }); 88 | $result = $matcher->setContext($context); 89 | } 90 | 91 | /** 92 | * @covers Silex\LazyUrlMatcher::getContext 93 | */ 94 | public function testGetContextIsProxy() 95 | { 96 | $context = $this->getMock('Symfony\Component\Routing\RequestContext'); 97 | $urlMatcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface'); 98 | $urlMatcher->expects($this->once()) 99 | ->method('getContext') 100 | ->will($this->returnValue($context)); 101 | 102 | $matcher = new LazyUrlMatcher(function () use ($urlMatcher) { 103 | return $urlMatcher; 104 | }); 105 | $resultContext = $matcher->getContext(); 106 | 107 | $this->assertSame($resultContext, $context); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /tests/Silex/Tests/LocaleTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests; 13 | 14 | use Silex\Application; 15 | use Symfony\Component\HttpFoundation\Request; 16 | use Symfony\Component\HttpKernel\HttpKernelInterface; 17 | 18 | /** 19 | * Locale test cases. 20 | * 21 | * @author Fabien Potencier 22 | */ 23 | class LocaleTest extends \PHPUnit_Framework_TestCase 24 | { 25 | public function testLocale() 26 | { 27 | $app = new Application(); 28 | $app->get('/', function (Request $request) { return $request->getLocale(); }); 29 | $response = $app->handle(Request::create('/')); 30 | $this->assertEquals('en', $response->getContent()); 31 | 32 | $app = new Application(); 33 | $app['locale'] = 'fr'; 34 | $app->get('/', function (Request $request) { return $request->getLocale(); }); 35 | $response = $app->handle(Request::create('/')); 36 | $this->assertEquals('fr', $response->getContent()); 37 | 38 | $app = new Application(); 39 | $app->get('/{_locale}', function (Request $request) { return $request->getLocale(); }); 40 | $response = $app->handle(Request::create('/es')); 41 | $this->assertEquals('es', $response->getContent()); 42 | } 43 | 44 | public function testLocaleInSubRequests() 45 | { 46 | $app = new Application(); 47 | $app->get('/embed/{_locale}', function (Request $request) { return $request->getLocale(); }); 48 | $app->get('/{_locale}', function (Request $request) use ($app) { 49 | return $request->getLocale().$app->handle(Request::create('/embed/es'), HttpKernelInterface::SUB_REQUEST)->getContent().$request->getLocale(); 50 | }); 51 | $response = $app->handle(Request::create('/fr')); 52 | $this->assertEquals('fresfr', $response->getContent()); 53 | 54 | $app = new Application(); 55 | $app->get('/embed', function (Request $request) { return $request->getLocale(); }); 56 | $app->get('/{_locale}', function (Request $request) use ($app) { 57 | return $request->getLocale().$app->handle(Request::create('/embed'), HttpKernelInterface::SUB_REQUEST)->getContent().$request->getLocale(); 58 | }); 59 | $response = $app->handle(Request::create('/fr')); 60 | // locale in sub-request must be "en" as this is the value if the sub-request is converted to an ESI 61 | $this->assertEquals('frenfr', $response->getContent()); 62 | } 63 | 64 | public function testLocaleWithBefore() 65 | { 66 | $app = new Application(); 67 | $app->before(function (Request $request) use ($app) { $request->setLocale('fr'); }); 68 | $app->get('/embed', function (Request $request) { return $request->getLocale(); }); 69 | $app->get('/', function (Request $request) use ($app) { 70 | return $request->getLocale().$app->handle(Request::create('/embed'), HttpKernelInterface::SUB_REQUEST)->getContent().$request->getLocale(); 71 | }); 72 | $response = $app->handle(Request::create('/')); 73 | // locale in sub-request is "en" as the before filter is only executed for the main request 74 | $this->assertEquals('frenfr', $response->getContent()); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Provider/DoctrineServiceProviderTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\DoctrineServiceProvider; 16 | 17 | /** 18 | * DoctrineProvider test cases. 19 | * 20 | * Fabien Potencier 21 | */ 22 | class DoctrineServiceProviderTest extends \PHPUnit_Framework_TestCase 23 | { 24 | public function setUp() 25 | { 26 | if (!is_dir(__DIR__.'/../../../../vendor/doctrine/common/lib') || !is_dir(__DIR__.'/../../../../vendor/doctrine/dbal/lib')) { 27 | $this->markTestSkipped('Doctrine Common/DBAL dependencies were not installed.'); 28 | } 29 | } 30 | 31 | public function testOptionsInitializer() 32 | { 33 | $app = new Application(); 34 | $app->register(new DoctrineServiceProvider()); 35 | 36 | $this->assertEquals($app['db.default_options'], $app['db']->getParams()); 37 | } 38 | 39 | public function testSingleConnection() 40 | { 41 | $app = new Application(); 42 | $app->register(new DoctrineServiceProvider(), array( 43 | 'db.options' => array('driver' => 'pdo_sqlite', 'memory' => true), 44 | )); 45 | 46 | $db = $app['db']; 47 | $params = $db->getParams(); 48 | $this->assertTrue(array_key_exists('memory', $params)); 49 | $this->assertTrue($params['memory']); 50 | $this->assertInstanceof('Doctrine\DBAL\Driver\PDOSqlite\Driver', $db->getDriver()); 51 | $this->assertEquals(22, $app['db']->fetchColumn('SELECT 22')); 52 | 53 | $this->assertSame($app['dbs']['default'], $db); 54 | } 55 | 56 | public function testMultipleConnections() 57 | { 58 | $app = new Application(); 59 | $app->register(new DoctrineServiceProvider(), array( 60 | 'dbs.options' => array( 61 | 'sqlite1' => array('driver' => 'pdo_sqlite', 'memory' => true), 62 | 'sqlite2' => array('driver' => 'pdo_sqlite', 'path' => sys_get_temp_dir().'/silex'), 63 | ), 64 | )); 65 | 66 | $db = $app['db']; 67 | $params = $db->getParams(); 68 | $this->assertTrue(array_key_exists('memory', $params)); 69 | $this->assertTrue($params['memory']); 70 | $this->assertInstanceof('Doctrine\DBAL\Driver\PDOSqlite\Driver', $db->getDriver()); 71 | $this->assertEquals(22, $app['db']->fetchColumn('SELECT 22')); 72 | 73 | $this->assertSame($app['dbs']['sqlite1'], $db); 74 | 75 | $db2 = $app['dbs']['sqlite2']; 76 | $params = $db2->getParams(); 77 | $this->assertTrue(array_key_exists('path', $params)); 78 | $this->assertEquals(sys_get_temp_dir().'/silex', $params['path']); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Provider/HttpCacheServiceProviderTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\HttpCacheServiceProvider; 16 | 17 | use Symfony\Component\HttpFoundation\Request; 18 | use Symfony\Component\HttpFoundation\Response; 19 | 20 | /** 21 | * HttpCacheProvider test cases. 22 | * 23 | * @author Igor Wiedler 24 | */ 25 | class HttpCacheServiceProviderTest extends \PHPUnit_Framework_TestCase 26 | { 27 | public function testRegister() 28 | { 29 | $app = new Application(); 30 | 31 | $app->register(new HttpCacheServiceProvider(), array( 32 | 'http_cache.cache_dir' => sys_get_temp_dir().'/silex_http_cache_'.uniqid(), 33 | )); 34 | 35 | $this->assertInstanceOf('Silex\HttpCache', $app['http_cache']); 36 | 37 | return $app; 38 | } 39 | 40 | /** 41 | * @depends testRegister 42 | */ 43 | public function testRunCallsShutdown($app) 44 | { 45 | $finished = false; 46 | 47 | $app->finish(function () use (&$finished) { 48 | $finished = true; 49 | }); 50 | 51 | $app->get('/', function () use ($app) { 52 | return new UnsendableResponse('will do something after finish'); 53 | }); 54 | 55 | $request = Request::create('/'); 56 | $app['http_cache']->run($request); 57 | 58 | $this->assertTrue($finished); 59 | } 60 | } 61 | 62 | class UnsendableResponse extends Response 63 | { 64 | public function send() 65 | { 66 | // do nothing 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Provider/MonologServiceProviderTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Provider; 13 | 14 | use Monolog\Handler\TestHandler; 15 | use Monolog\Logger; 16 | use Silex\Application; 17 | use Silex\Provider\MonologServiceProvider; 18 | use Symfony\Component\HttpFoundation\Request; 19 | 20 | /** 21 | * MonologProvider test cases. 22 | * 23 | * @author Igor Wiedler 24 | */ 25 | class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase 26 | { 27 | public function setUp() 28 | { 29 | if (!is_dir(__DIR__.'/../../../../vendor/monolog/monolog/src')) { 30 | $this->markTestSkipped('Monolog dependency was not installed.'); 31 | } 32 | } 33 | 34 | public function testRequestLogging() 35 | { 36 | $app = $this->getApplication(); 37 | 38 | $app->get('/foo', function () use ($app) { 39 | return 'foo'; 40 | }); 41 | 42 | $this->assertFalse($app['monolog.handler']->hasInfoRecords()); 43 | 44 | $request = Request::create('/foo'); 45 | $app->handle($request); 46 | 47 | $this->assertTrue($app['monolog.handler']->hasInfo('> GET /foo')); 48 | $this->assertTrue($app['monolog.handler']->hasInfo('< 200')); 49 | $this->assertTrue($app['monolog.handler']->hasInfo('Matched route "GET_foo" (parameters: "_controller": "{}", "_route": "GET_foo")')); 50 | } 51 | 52 | public function testManualLogging() 53 | { 54 | $app = $this->getApplication(); 55 | 56 | $app->get('/log', function () use ($app) { 57 | $app['monolog']->addDebug('logging a message'); 58 | }); 59 | 60 | $this->assertFalse($app['monolog.handler']->hasDebugRecords()); 61 | 62 | $request = Request::create('/log'); 63 | $app->handle($request); 64 | 65 | $this->assertTrue($app['monolog.handler']->hasDebug('logging a message')); 66 | } 67 | 68 | public function testErrorLogging() 69 | { 70 | $app = $this->getApplication(); 71 | 72 | $app->error(function (\Exception $e) { 73 | return 'error handled'; 74 | }); 75 | 76 | /** 77 | * Simulate 404, logged to error level 78 | */ 79 | $this->assertFalse($app['monolog.handler']->hasErrorRecords()); 80 | 81 | $request = Request::create('/error'); 82 | $app->handle($request); 83 | 84 | $records = $app['monolog.handler']->getRecords(); 85 | $pattern = "#Symfony\\\\Component\\\\HttpKernel\\\\Exception\\\\NotFoundHttpException: No route found for \"GET /error\" \(uncaught exception\) at .* line \d+#"; 86 | $this->assertMatchingRecord($pattern, Logger::ERROR, $app['monolog.handler']); 87 | 88 | /** 89 | * Simulate unhandled exception, logged to critical 90 | */ 91 | $app->get('/error', function () { 92 | throw new \RuntimeException('very bad error'); 93 | }); 94 | 95 | $this->assertFalse($app['monolog.handler']->hasCriticalRecords()); 96 | 97 | $request = Request::create('/error'); 98 | $app->handle($request); 99 | 100 | $pattern = "#RuntimeException: very bad error \(uncaught exception\) at .*Silex/Tests/Provider/MonologServiceProviderTest\.php line \d+#"; 101 | $this->assertMatchingRecord($pattern, Logger::CRITICAL, $app['monolog.handler']); 102 | } 103 | 104 | protected function assertMatchingRecord($pattern, $level, $handler) 105 | { 106 | $found = false; 107 | $records = $handler->getRecords(); 108 | foreach ($records as $record) { 109 | if (preg_match($pattern, $record['message']) && $record['level'] == $level) { 110 | $found = true; 111 | continue; 112 | } 113 | } 114 | $this->assertTrue($found, "Trying to find record matching $pattern with level $level"); 115 | } 116 | 117 | protected function getApplication() 118 | { 119 | $app = new Application(); 120 | 121 | $app->register(new MonologServiceProvider()); 122 | 123 | $app['monolog.handler'] = $app->share(function () use ($app) { 124 | return new TestHandler($app['monolog.level']); 125 | }); 126 | 127 | return $app; 128 | } 129 | } 130 | 131 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Provider/SerializerServiceProviderTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\SerializerServiceProvider; 16 | 17 | /** 18 | * SerializerServiceProvider test cases. 19 | * 20 | * @author Fabien Potencier 21 | */ 22 | class SerializerServiceProviderTest extends \PHPUnit_Framework_TestCase 23 | { 24 | public function testRegister() 25 | { 26 | $app = new Application(); 27 | 28 | $app->register(new SerializerServiceProvider); 29 | 30 | $this->assertInstanceOf("Symfony\Component\Serializer\Serializer", $app['serializer']); 31 | $this->assertTrue($app['serializer']->supportsEncoding('xml')); 32 | $this->assertTrue($app['serializer']->supportsEncoding('json')); 33 | $this->assertTrue($app['serializer']->supportsDecoding('xml')); 34 | $this->assertTrue($app['serializer']->supportsDecoding('json')); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Provider/SessionServiceProviderTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\WebTestCase; 16 | use Silex\Provider\SessionServiceProvider; 17 | use Symfony\Component\HttpKernel\Client; 18 | 19 | /** 20 | * SessionProvider test cases. 21 | * 22 | * @author Igor Wiedler 23 | * @author Fabien Potencier 24 | */ 25 | class SessionServiceProviderTest extends WebTestCase 26 | { 27 | public function testRegister() 28 | { 29 | /** 30 | * Smoke test 31 | */ 32 | $defaultStorage = $this->app['session.storage.native']; 33 | 34 | $client = $this->createClient(); 35 | 36 | $client->request('get', '/login'); 37 | $this->assertEquals('Logged in successfully.', $client->getResponse()->getContent()); 38 | 39 | $client->request('get', '/account'); 40 | $this->assertEquals('This is your account.', $client->getResponse()->getContent()); 41 | 42 | $client->request('get', '/logout'); 43 | $this->assertEquals('Logged out successfully.', $client->getResponse()->getContent()); 44 | 45 | $client->request('get', '/account'); 46 | $this->assertEquals('You are not logged in.', $client->getResponse()->getContent()); 47 | } 48 | 49 | public function createApplication() 50 | { 51 | $app = new Application(); 52 | 53 | $app->register(new SessionServiceProvider(), array( 54 | 'session.test' => true, 55 | )); 56 | 57 | $app->get('/login', function () use ($app) { 58 | $app['session']->set('logged_in', true); 59 | 60 | return 'Logged in successfully.'; 61 | }); 62 | 63 | $app->get('/account', function () use ($app) { 64 | if (!$app['session']->get('logged_in')) { 65 | return 'You are not logged in.'; 66 | } 67 | 68 | return 'This is your account.'; 69 | }); 70 | 71 | $app->get('/logout', function () use ($app) { 72 | $app['session']->invalidate(); 73 | 74 | return 'Logged out successfully.'; 75 | }); 76 | 77 | return $app; 78 | } 79 | 80 | public function testWithRoutesThatDoesNotUseSession() 81 | { 82 | $app = new Application(); 83 | 84 | $app->register(new SessionServiceProvider(), array( 85 | 'session.test' => true, 86 | )); 87 | 88 | $app->get('/', function () { 89 | return 'A welcome page.'; 90 | }); 91 | 92 | $app->get('/robots.txt', function () { 93 | return 'Informations for robots.'; 94 | }); 95 | 96 | $app['debug'] = true; 97 | $app['exception_handler']->disable(); 98 | 99 | $client = new Client($app); 100 | 101 | $client->request('get', '/'); 102 | $this->assertEquals('A welcome page.', $client->getResponse()->getContent()); 103 | 104 | $client->request('get', '/robots.txt'); 105 | $this->assertEquals('Informations for robots.', $client->getResponse()->getContent()); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Provider/SpoolStub.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Provider; 13 | 14 | class SpoolStub implements \Swift_Spool 15 | { 16 | private $messages = array(); 17 | public $hasFlushed = false; 18 | 19 | public function getMessages() 20 | { 21 | return $this->messages; 22 | } 23 | 24 | public function start() 25 | { 26 | } 27 | 28 | public function stop() 29 | { 30 | } 31 | 32 | public function isStarted() 33 | { 34 | return count($this->messages) > 0; 35 | } 36 | 37 | public function queueMessage(\Swift_Mime_Message $message) 38 | { 39 | $this->messages[] = $message; 40 | } 41 | 42 | public function flushQueue(\Swift_Transport $transport, &$failedRecipients = null) 43 | { 44 | $this->hasFlushed = true; 45 | $this->messages = array(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Provider/SwiftmailerServiceProviderTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\SwiftmailerServiceProvider; 16 | 17 | use Symfony\Component\HttpFoundation\Request; 18 | use Symfony\Component\HttpFoundation\Response; 19 | 20 | class SwiftmailerServiceProviderTest extends \PHPUnit_Framework_TestCase 21 | { 22 | public function setUp() 23 | { 24 | if (!is_dir(__DIR__.'/../../../../vendor/swiftmailer/swiftmailer/lib')) { 25 | $this->markTestSkipped('Swiftmailer dependency was not installed.'); 26 | } 27 | } 28 | 29 | public function testSwiftMailerServiceIsSwiftMailer() 30 | { 31 | $app = new Application(); 32 | 33 | $app->register(new SwiftmailerServiceProvider()); 34 | $app->boot(); 35 | 36 | $this->assertInstanceOf('Swift_Mailer', $app['mailer']); 37 | } 38 | 39 | public function testSwiftMailerSendsMailsOnFinish() 40 | { 41 | $app = new Application(); 42 | 43 | $app->register(new SwiftmailerServiceProvider()); 44 | $app->boot(); 45 | 46 | $app['swiftmailer.spool'] = $app->share(function () { 47 | return new SpoolStub(); 48 | }); 49 | 50 | $app->get('/', function() use ($app) { 51 | $app['mailer']->send(\Swift_Message::newInstance()); 52 | }); 53 | 54 | $this->assertCount(0, $app['swiftmailer.spool']->getMessages()); 55 | 56 | $request = Request::create('/'); 57 | $response = $app->handle($request); 58 | $this->assertCount(1, $app['swiftmailer.spool']->getMessages()); 59 | 60 | $app->terminate($request, $response); 61 | $this->assertTrue($app['swiftmailer.spool']->hasFlushed); 62 | $this->assertCount(0, $app['swiftmailer.spool']->getMessages()); 63 | } 64 | 65 | public function testSwiftMailerAvoidsFlushesIfMailerIsUnused() 66 | { 67 | $app = new Application(); 68 | 69 | $app->register(new SwiftmailerServiceProvider()); 70 | $app->boot(); 71 | 72 | $app['swiftmailer.spool'] = $app->share(function () { 73 | return new SpoolStub(); 74 | }); 75 | 76 | $app->get('/', function() use ($app) { }); 77 | 78 | $request = Request::create('/'); 79 | $response = $app->handle($request); 80 | $this->assertCount(0, $app['swiftmailer.spool']->getMessages()); 81 | 82 | $app->terminate($request, $response); 83 | $this->assertFalse($app['swiftmailer.spool']->hasFlushed); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Provider/TwigServiceProviderTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\TwigServiceProvider; 16 | 17 | use Symfony\Component\HttpFoundation\Request; 18 | 19 | /** 20 | * TwigProvider test cases. 21 | * 22 | * @author Igor Wiedler 23 | */ 24 | class TwigServiceProviderTest extends \PHPUnit_Framework_TestCase 25 | { 26 | public function setUp() 27 | { 28 | if (!is_dir(__DIR__.'/../../../../vendor/twig/twig/lib')) { 29 | $this->markTestSkipped('Twig dependency was not installed.'); 30 | } 31 | } 32 | 33 | public function testRegisterAndRender() 34 | { 35 | $app = new Application(); 36 | 37 | $app->register(new TwigServiceProvider(), array( 38 | 'twig.templates' => array('hello' => 'Hello {{ name }}!'), 39 | )); 40 | 41 | $app->get('/hello/{name}', function ($name) use ($app) { 42 | return $app['twig']->render('hello', array('name' => $name)); 43 | }); 44 | 45 | $request = Request::create('/hello/john'); 46 | $response = $app->handle($request); 47 | $this->assertEquals('Hello john!', $response->getContent()); 48 | } 49 | 50 | public function testRenderFunction() 51 | { 52 | $app = new Application(); 53 | 54 | $app->register(new TwigServiceProvider(), array( 55 | 'twig.templates' => array( 56 | 'hello' => '{{ render("/foo") }}', 57 | 'foo' => 'foo', 58 | ), 59 | )); 60 | 61 | $app->get('/hello', function () use ($app) { 62 | return $app['twig']->render('hello'); 63 | }); 64 | 65 | $app->get('/foo', function () use ($app) { 66 | return $app['twig']->render('foo'); 67 | }); 68 | 69 | $request = Request::create('/hello'); 70 | $response = $app->handle($request); 71 | $this->assertEquals('foo', $response->getContent()); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Provider/UrlGeneratorServiceProviderTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\UrlGeneratorServiceProvider; 16 | 17 | use Symfony\Component\HttpFoundation\Request; 18 | 19 | /** 20 | * UrlGeneratorProvider test cases. 21 | * 22 | * @author Igor Wiedler 23 | */ 24 | class UrlGeneratorServiceProviderTest extends \PHPUnit_Framework_TestCase 25 | { 26 | public function testRegister() 27 | { 28 | $app = new Application(); 29 | 30 | $app->register(new UrlGeneratorServiceProvider()); 31 | 32 | $app->get('/hello/{name}', function ($name) {}) 33 | ->bind('hello'); 34 | 35 | $app->get('/', function () {}); 36 | 37 | $request = Request::create('/'); 38 | $app->handle($request); 39 | 40 | $this->assertInstanceOf('Symfony\Component\Routing\Generator\UrlGenerator', $app['url_generator']); 41 | } 42 | 43 | public function testUrlGeneration() 44 | { 45 | $app = new Application(); 46 | 47 | $app->register(new UrlGeneratorServiceProvider()); 48 | 49 | $app->get('/hello/{name}', function ($name) {}) 50 | ->bind('hello'); 51 | 52 | $app->get('/', function () use ($app) { 53 | return $app['url_generator']->generate('hello', array('name' => 'john')); 54 | }); 55 | 56 | $request = Request::create('/'); 57 | $response = $app->handle($request); 58 | 59 | $this->assertEquals('/hello/john', $response->getContent()); 60 | } 61 | 62 | public function testAbsoluteUrlGeneration() 63 | { 64 | $app = new Application(); 65 | 66 | $app->register(new UrlGeneratorServiceProvider()); 67 | 68 | $app->get('/hello/{name}', function ($name) {}) 69 | ->bind('hello'); 70 | 71 | $app->get('/', function () use ($app) { 72 | return $app['url_generator']->generate('hello', array('name' => 'john'), true); 73 | }); 74 | 75 | $request = Request::create('https://localhost:81/'); 76 | $response = $app->handle($request); 77 | 78 | $this->assertEquals('https://localhost:81/hello/john', $response->getContent()); 79 | } 80 | 81 | public function testUrlGenerationWithHttp() 82 | { 83 | $app = new Application(); 84 | 85 | $app->register(new UrlGeneratorServiceProvider()); 86 | 87 | $app->get('/insecure', function () {}) 88 | ->bind('insecure_page') 89 | ->requireHttp(); 90 | 91 | $app->get('/', function () use ($app) { 92 | return $app['url_generator']->generate('insecure_page'); 93 | }); 94 | 95 | $request = Request::create('https://localhost/'); 96 | $response = $app->handle($request); 97 | 98 | $this->assertEquals('http://localhost/insecure', $response->getContent()); 99 | } 100 | 101 | public function testUrlGenerationWithHttps() 102 | { 103 | $app = new Application(); 104 | 105 | $app->register(new UrlGeneratorServiceProvider()); 106 | 107 | $app->get('/secure', function () {}) 108 | ->bind('secure_page') 109 | ->requireHttps(); 110 | 111 | $app->get('/', function () use ($app) { 112 | return $app['url_generator']->generate('secure_page'); 113 | }); 114 | 115 | $request = Request::create('http://localhost/'); 116 | $response = $app->handle($request); 117 | 118 | $this->assertEquals('https://localhost/secure', $response->getContent()); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Provider/ValidatorServiceProviderTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Provider; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\ValidatorServiceProvider; 16 | use Silex\Provider\FormServiceProvider; 17 | use Symfony\Component\Validator\Constraints as Assert; 18 | 19 | /** 20 | * ValidatorServiceProvider 21 | * 22 | * Javier Lopez 23 | */ 24 | class ValidatorServiceProviderTest extends \PHPUnit_Framework_TestCase 25 | { 26 | public function setUp() 27 | { 28 | if (!is_dir(__DIR__.'/../../../../vendor/symfony/validator')) { 29 | $this->markTestSkipped('Validator dependency was not installed.'); 30 | } 31 | } 32 | 33 | public function testRegister() 34 | { 35 | $app = new Application(); 36 | 37 | $app->register(new ValidatorServiceProvider()); 38 | 39 | return $app; 40 | } 41 | 42 | /** 43 | * @depends testRegister 44 | */ 45 | public function testValidatorServiceIsAValidator($app) 46 | { 47 | $this->assertInstanceOf('Symfony\Component\Validator\Validator', $app['validator']); 48 | } 49 | 50 | /** 51 | * @depends testRegister 52 | * @dataProvider testValidatorConstraintProvider 53 | */ 54 | public function testValidatorConstraint($email, $isValid, $nbGlobalError, $nbEmailError, $app) 55 | { 56 | if (!is_dir(__DIR__ . '/../../../../vendor/symfony/form')) { 57 | $this->markTestSkipped('Form component was not installed.'); 58 | } 59 | 60 | $app->register(new ValidatorServiceProvider()); 61 | $app->register(new FormServiceProvider()); 62 | 63 | $constraints = new Assert\Collection(array( 64 | 'email' => array( 65 | new Assert\NotBlank(), 66 | new Assert\Email(), 67 | ), 68 | )); 69 | 70 | $builder = $app['form.factory']->createBuilder('form', array(), array( 71 | 'validation_constraint' => $constraints, 72 | 'csrf_protection' => false, 73 | )); 74 | 75 | $form = $builder 76 | ->add('email', 'email', array('label' => 'Email')) 77 | ->getForm() 78 | ; 79 | 80 | $form->bind(array('email' => $email)); 81 | 82 | $this->assertEquals($isValid, $form->isValid()); 83 | $this->assertEquals($nbGlobalError, count($form->getErrors())); 84 | $this->assertEquals($nbEmailError, count($form->offsetGet('email')->getErrors())); 85 | } 86 | 87 | public function testValidatorConstraintProvider() 88 | { 89 | // Email, form is valid , nb global error, nb email error 90 | return array( 91 | array('', false, 0, 1), 92 | array('not an email', false, 0, 1), 93 | array('email@sample.com', true, 0, 0), 94 | ); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Route/SecurityRoute.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Route; 13 | 14 | use Silex\Route; 15 | 16 | class SecurityRoute extends Route 17 | { 18 | use Route\SecurityTrait; 19 | } 20 | -------------------------------------------------------------------------------- /tests/Silex/Tests/Route/SecurityTraitTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests\Route; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\SecurityServiceProvider; 16 | use Symfony\Component\HttpFoundation\Request; 17 | 18 | /** 19 | * SecurityTrait test cases. 20 | * 21 | * @author Fabien Potencier 22 | */ 23 | class SecurityTraitTest extends \PHPUnit_Framework_TestCase 24 | { 25 | public function setUp() 26 | { 27 | if (version_compare(phpversion(), '5.4.0', '<')) { 28 | $this->markTestSkipped('PHP 5.4 is required for this test'); 29 | } 30 | 31 | if (!is_dir(__DIR__.'/../../../../vendor/symfony/security')) { 32 | $this->markTestSkipped('Security dependency was not installed.'); 33 | } 34 | } 35 | 36 | public function testSecure() 37 | { 38 | $app = new Application(); 39 | $app['route_class'] = 'Silex\Tests\Route\SecurityRoute'; 40 | $app->register(new SecurityServiceProvider(), array( 41 | 'security.firewalls' => array( 42 | 'default' => array( 43 | 'http' => true, 44 | 'users' => array( 45 | 'fabien' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), 46 | ), 47 | ), 48 | ), 49 | )); 50 | 51 | $app->get('/', function () { return 'foo'; }) 52 | ->secure('ROLE_ADMIN') 53 | ; 54 | 55 | $request = Request::create('/'); 56 | $response = $app->handle($request); 57 | $this->assertEquals(401, $response->getStatusCode()); 58 | 59 | $request = Request::create('/'); 60 | $request->headers->set('PHP_AUTH_USER', 'fabien'); 61 | $request->headers->set('PHP_AUTH_PW', 'foo'); 62 | $response = $app->handle($request); 63 | $this->assertEquals(200, $response->getStatusCode()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/Silex/Tests/ServiceControllerResolverRouterTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests; 13 | 14 | use Silex\Application; 15 | use Silex\Provider\ServiceControllerServiceProvider; 16 | use Symfony\Component\HttpFoundation\Request; 17 | 18 | /** 19 | * Router test cases, using the ServiceControllerResolver 20 | */ 21 | class ServiceControllerResolverRouterTest extends RouterTest 22 | { 23 | public function testServiceNameControllerSyntax() 24 | { 25 | $app = new Application(); 26 | 27 | $app['service_name'] = function () { 28 | return new MyController; 29 | }; 30 | 31 | $app->get('/bar', 'service_name:getBar'); 32 | 33 | $this->checkRouteResponse($app, '/bar', 'bar'); 34 | } 35 | 36 | protected function checkRouteResponse($app, $path, $expectedContent, $method = 'get', $message = null) 37 | { 38 | $app->register(new ServiceControllerServiceProvider()); 39 | 40 | $request = Request::create($path, $method); 41 | $response = $app->handle($request); 42 | $this->assertEquals($expectedContent, $response->getContent(), $message); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Silex/Tests/ServiceControllerResolverTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Silex\Tests; 13 | 14 | use Silex\ServiceControllerResolver; 15 | use Silex\Application; 16 | use Symfony\Component\HttpFoundation\Request; 17 | 18 | /** 19 | * Unit tests for ServiceControllerResolver, see ServiceControllerResolverRouterTest for some 20 | * integration tests 21 | */ 22 | class ServiceControllerResolverTest extends \PHPUnit_Framework_Testcase 23 | { 24 | public function setup() 25 | { 26 | $this->mockResolver = $this->getMockBuilder('Symfony\Component\HttpKernel\Controller\ControllerResolverInterface') 27 | ->disableOriginalConstructor() 28 | ->getMock(); 29 | 30 | $this->app = new Application(); 31 | $this->resolver = new ServiceControllerResolver($this->mockResolver, $this->app); 32 | } 33 | 34 | public function testShouldResolveServiceController() 35 | { 36 | $this->app['some_service'] = function() { return new \stdClass(); }; 37 | 38 | $req = Request::create('/'); 39 | $req->attributes->set('_controller', 'some_service:methodName'); 40 | 41 | $this->assertEquals( 42 | array($this->app['some_service'], 'methodName'), 43 | $this->resolver->getController($req) 44 | ); 45 | } 46 | 47 | public function testShouldDelegateNonStrings() 48 | { 49 | $req = Request::create('/'); 50 | $req->attributes->set('_controller', function() {}); 51 | 52 | $this->mockResolver->expects($this->once()) 53 | ->method('getController') 54 | ->with($req) 55 | ->will($this->returnValue(123)); 56 | 57 | $this->assertEquals(123, $this->resolver->getController($req)); 58 | } 59 | 60 | /** 61 | * Note: This doesn't test the regex extensively, just a common use case 62 | */ 63 | public function testShouldDelegateNonMatchingSyntax() 64 | { 65 | $req = Request::create('/'); 66 | $req->attributes->set('_controller', 'some_class::methodName'); 67 | 68 | $this->mockResolver->expects($this->once()) 69 | ->method('getController') 70 | ->with($req) 71 | ->will($this->returnValue(123)); 72 | 73 | $this->assertEquals(123, $this->resolver->getController($req)); 74 | } 75 | 76 | /** 77 | * @expectedException InvalidArgumentException 78 | * @expectedExceptionMessage Service "some_service" does not exist. 79 | */ 80 | public function testShouldThrowIfServiceIsMissing() 81 | { 82 | $req = Request::create('/'); 83 | $req->attributes->set('_controller', 'some_service:methodName'); 84 | $this->resolver->getController($req); 85 | } 86 | 87 | public function testShouldDelegateGetArguments() 88 | { 89 | $req = Request::create('/'); 90 | $this->mockResolver->expects($this->once()) 91 | ->method('getArguments') 92 | ->with($req) 93 | ->will($this->returnValue(123)); 94 | 95 | $this->assertEquals(123, $this->resolver->getArguments($req, function() {})); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tests/Silex/Tests/StreamTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests; 13 | 14 | use Silex\Application; 15 | 16 | use Symfony\Component\HttpFoundation\Request; 17 | use Symfony\Component\HttpFoundation\StreamedResponse; 18 | 19 | /** 20 | * Stream test cases. 21 | * 22 | * @author Igor Wiedler 23 | */ 24 | class StreamTest extends \PHPUnit_Framework_TestCase 25 | { 26 | public function testStreamReturnsStreamingResponse() 27 | { 28 | $app = new Application(); 29 | 30 | $response = $app->stream(); 31 | $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response); 32 | $this->assertSame(false, $response->getContent()); 33 | } 34 | 35 | public function testStreamActuallyStreams() 36 | { 37 | $i = 0; 38 | 39 | $stream = function () use (&$i) { 40 | $i++; 41 | }; 42 | 43 | $app = new Application(); 44 | $response = $app->stream($stream); 45 | 46 | $this->assertEquals(0, $i); 47 | 48 | $request = Request::create('/stream'); 49 | $response->prepare($request); 50 | $response->sendContent(); 51 | 52 | $this->assertEquals(1, $i); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/Silex/Tests/WebTestCaseTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Silex\Tests; 13 | 14 | use Silex\Application; 15 | use Silex\WebTestCase; 16 | 17 | /** 18 | * Functional test cases. 19 | * 20 | * @author Igor Wiedler 21 | */ 22 | class WebTestCaseTest extends WebTestCase 23 | { 24 | public function createApplication() 25 | { 26 | $app = new Application(); 27 | 28 | $app->match('/hello', function () { 29 | return 'world'; 30 | }); 31 | 32 | $app->match('/html', function () { 33 | return '

title

'; 34 | }); 35 | 36 | $app->match('/server', function () use ($app) { 37 | $user = $app['request']->server->get('PHP_AUTH_USER'); 38 | $pass = $app['request']->server->get('PHP_AUTH_PW'); 39 | 40 | return "

$user:$pass

"; 41 | }); 42 | 43 | return $app; 44 | } 45 | 46 | public function testGetHello() 47 | { 48 | $client = $this->createClient(); 49 | 50 | $client->request('GET', '/hello'); 51 | $response = $client->getResponse(); 52 | $this->assertTrue($response->isSuccessful()); 53 | $this->assertEquals('world', $response->getContent()); 54 | } 55 | 56 | public function testCrawlerFilter() 57 | { 58 | $client = $this->createClient(); 59 | 60 | $crawler = $client->request('GET', '/html'); 61 | $this->assertEquals('title', $crawler->filter('h1')->text()); 62 | } 63 | 64 | public function testServerVariables() 65 | { 66 | $user = 'klaus'; 67 | $pass = '123456'; 68 | 69 | $client = $this->createClient(array( 70 | 'PHP_AUTH_USER' => $user, 71 | 'PHP_AUTH_PW' => $pass, 72 | )); 73 | 74 | $crawler = $client->request('GET', '/server'); 75 | $this->assertEquals("$user:$pass", $crawler->filter('h1')->text()); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | add('Silex\Tests', __DIR__); 5 | --------------------------------------------------------------------------------