Professional Documents
Culture Documents
SILEX
PHP micro-framework
http://andreiabohner.org
Install
nginx
Usage
server {
#site root is redirected to the app boot script
location = / {
try_files @site @site;
}
// web/index.php
require_once __DIR__.'/../vendor/autoload.php';
$app->run();
location @site {
fastcgi_pass unix:/var/run/php-fpm/www.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
#uncomment when running via https
#fastcgi_param HTTPS on;
}
web.config file:
<?xml version="1.0"?>
<configuration>
<system.webServer>
<defaultDocument>
<files>
<clear />
<add value="index.php" />
</files>
</defaultDocument>
<rewrite>
<rules>
<rule name="Silex Front Controller"
stopProcessing="true">
<match url="^(.*)$" ignoreCase="false" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}"
matchType="IsFile" ignoreCase="false"
negate="true" />
</conditions>
<action type="Rewrite" url="index.php"
appendQueryString="true" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
IIS
server.document-root = "/path/to/app"
url.rewrite-once = (
# configure some static files
"^/assets/.+" => "$0",
"^/favicon\.ico$" => "$0",
)
only for
development!
App Configuration
Default Configuration
Configuration
Default value
$app['debug']
$app['request.http_port']
$app['request.https_port']
$app['charset']
$app['locale']
$app['logger']
false
80
443
UTF-8
en
null
Set
Get
$app['debug'] = true;
// turn on the debug mode
$debug = $app['debug'];
Get
Global Configuration
Stream
Streaming response (when you cannot buffer the data being sent)
$app['controllers']
applied to already registered
->value('id', '1')
controllers and become the
->assert('id', '\d+')
defaults for new controllers
->requireHttps()
->method('get')
->convert('id', function () {/* ... */ })
->before(function () {/* ... */ });
Symfony2 Components
Symfony2 components used by Silex:
HttpFoundation
HttpKernel
Routing
EventDispatcher
$app->redirect('/account');
{{ app.asset_path }}/css/styles.css
Forward
Request::setTrustedProxies(array($ip));
$app->run();
while (!feof($fh)) {
echo fread($fh, 1024);
ob_flush();
flush();
}
};
fclose($fh);
Abort
Stop the request early. It actually throws an exception
$app->abort(404, "Book $id does not exist.");
Sending a file
Redirect
use Symfony\Component\HttpFoundation\Request;
$stream = function () {
$fh = fopen('http://www.example.com/', 'rb');
$assetPath = $app['asset_path'];
JSON
Return JSON data and apply correct escape
$app->json($error, 404);
$app->json($user);
HttpFoundation 2.2+
Create a BinaryFileResponse
$app->sendFile('/base/path/' . $path)
->setContentDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
'pic.jpg')
Escape
Escape user input, to prevent Cross-Site-Scripting attacks
$app->escape($name)
App Events
EARLY_EVENT = 512;
LATE_EVENT = -512;
HTTP Methods
Routing
A route pattern consists of:
Pattern
POST
method
$app->get('/books/{id}', function () {
// ...
});
PUT
DELETE
Request::enableHttpMethodParameterOverride();
$app->run();
pattern
PATCH
return $output;
});
use Symfony\Component\HttpFoundation\Request;
Default Values
$app->match('/book, function () {
// ...
});
$app->match('/book', function () {
// ...
})
->method('PATCH');
Can be restricted
via the method
method
$app->match('/book', function () {
// ...
})
->method('PUT|POST');
The order of the routes is significant.
The first matching route will be used
(place more generic routes at the bottom)
Requirements
$app->get('/blog/{id}', function ($id) {
// ...
})
->assert('id', '\d+');
Chained requirements
$app->get('/blog/{postId}/{commentId}', function ($postId, $commentId) {
// ...
})
->assert('postId', '\d+')
->assert('commentId', '\d+');
Named Routes
$app->get('/', function () {
// ...
})
->bind('homepage');
$app->get('/blog/{id}', function ($id) {
// ...
})
->bind('blog_post');
It only makes sense to name routes if you use providers
that make use of the RouteCollection
Controllers in Classes
Group URLs (mount)
$app->get('/', 'Acme\\Foo::bar');
// define "global" controllers
// / map to the main home page
$app->get('/', function () {
return 'Main home page';
});
// /blog/ map to the blog home page
$app->mount('/blog', $blog);
// /forum/ map to the forum home page
$app->mount('/forum', $forum);
use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
namespace Acme
{
class Foo
{
public function bar(Request $request, Application $app)
{
// ...
For an even stronger separation between Silex and your
controllers, you can define your controllers as services
Error Handlers
To register an error handler, pass a closure to the
error method which takes an Exception argument
and returns a response:
use Symfony\Component\HttpFoundation\Response;
$app->error(function (\Exception $e, $code) {
return new Response('Something went terribly wrong.');
});
default:
$message = 'Something went terribly wrong.';
PHP 5.4+
Traits
The error handlers are also called when you use abort to
abort a request early:
$app->get('/blog/{id}', function (Silex\Application $app, $id)
use ($posts) {
if (!isset($posts[$id])) {
$app->abort(404, "Post $id does not exist.");
}
return new Response(...);
});
Middlewares
Application Middlewares
Only run
for master
request
Before
Event registered on the Symfony request event.
Run after the routing and security and before the controller.
$app->before(function (Request $req, Application $app) {
// ...
});
To run the middleware even if an exception is thrown
early on (on a 404 or 403 error for instance),
register it as an early event:
$app->before(function (Request $req, Application $app) {
// ...
}, Application::EARLY_EVENT);
After
Event registered on the Symfony response event.
Route Middlewares
Middlewares Priority
$app->get('/somewhere', function () {
// ...
})
->before($before1)
->before($before2)
->after($after1)
->after($after2)
;
Before
Fired before the route callback, but after the before
application middlewares:
$before = function (Request $req, Application $app) {
// ...
};
$app->get('/somewhere', function () {
// ...
})
->before($before);
Terminate
Event registered on the Symfony terminate event.
Run after the Response has been sent to the client
(like sending emails or logging).
$app->finish(function (Request $req, Response $resp) {
// ...
// Warning: modifications to the Request or Response
// will be ignored
});
After
Fired after the route callback, but before the application
after application middlewares:
$after = function (Request $req,
Response $resp,
Application $app) {
// ...
};
$app->get('/somewhere', function () {
// ...
})
->after($after);
Services
Definition
Core Services
request
routes
controllers
dispatcher
resolver
kernel
request_context
exception_handler
logger
Psr\Log\LoggerInterface instance.
By default, logging is disabled as the
value is set to null. To enable logging
either use the MonologServiceProvider
or define your own logger service that
conforms to the PSR logger interface.
In versions of Silex before 1.1 this must
be a Symfony\Component\HttpKernel\
Log\LoggerInterface.
are shared
closure
$app['some_service'] = function () {
return new Service();
};
Retrieve the service:
$service = $app['some_service'];
some_service depends
on some_other_service
Protected Closures
Shared Services
Use the same instance of a service across all of your code.
Create the service on first invocation, and then return the
existing instance on any subsequent access.
$app['some_service'] = $app->share(function () {
return new Service();
});
E.g. 2:
$app['asset_path'] = $app->share(function () {
// logic to determine the asset path
return 'http://assets.examples.com';
});
E.g. 3:
$app['user.persist_path'] = '/tmp/users';
$app['user.persister'] = $app->share(function ($app) {
return new JsonUserPersister($app['user.persist_path']);
});
protected closures
do not get access
to the container
Providers
Allow to reuse parts of an application into another one
Service Providers
Loading providers
In order to load and use a service provider,
register it on the application:
$app->register(new Acme\DatabaseServiceProvider());
E.g.:
namespace Acme;
use Silex\Application;
use Silex\ServiceProviderInterface;
class HelloServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['hello'] = $app->protect(function ($name) use ($app){
$default = $app['hello.default_name'] ?
$app['hello.default_name'] : ';
$name = $name ?: $default;
Creating a provider
Providers must implement the Silex\ServiceProviderInterface:
interface ServiceProviderInterface
{
public function register(Application $app);
public function boot(Application $app);
}
Just create a new class that implements the two methods:
register()
boot()
Controller Providers
Loading providers
To load and use a controller provider, "mount" its controllers
under a path:
$app->mount('/blog', new Acme\BlogControllerProvider());
All controllers defined by the provider will now be available
under the /blog path.
Creating a provider
Providers must implement the Silex\ControllerProviderInterface:
interface ControllerProviderInterface
{
public function connect(Application $app);
}
The connect method must return an instance of
ControllerCollection. ControllerCollection is the class
where all controller related methods are defined
(like get, post, match, ...).
namespace Acme;
use Silex\Application;
use Silex\ControllerProviderInterface;
Using:
return $controllers;
user
Included Providers
charset
path
port
DoctrineServiceProvider
MonologServiceProvider
SessionServiceProvider
SerializerServiceProvider
SwiftmailerServiceProvider
TwigServiceProvider
TranslationServiceProvider
UrlGeneratorServiceProvider
Services
ValidatorServiceProvider
HttpCacheServiceProvider
FormServiceProvider
SecurityServiceProvider
RememberMeServiceProvider
ServiceControllerServiceProvider
db.config
Parameters
db.options Array of Doctrine DBAL options.
These options are available:
driver
db
dbname
host
Registering
$app->register(new Silex\Provider\DoctrineServiceProvider(),
array(
'db.options' => array(
driver' => 'pdo_sqlite',
'path' => __DIR__.'/app.db',
),
));
Usage
$app->get('/blog/{id}', function ($id) use ($app) {
$sql = "SELECT * FROM posts WHERE id = ?";
$post = $app['db']->fetchAssoc($sql, array((int) $id));
return "<h1>{$post['title']}</h1>".
<p>{$post['body']}</p>";
});
Parameters
monolog.logfile
* monolog.bubble
* monolog.level
* monolog.name
(default: myapp)
Usage
$app->post('/user', function () use ($app) {
// ...
$app['monolog']->addInfo(sprintf("User '%s' registered.",
$username));
return new Response('', 201);
});
name
id
cookie_lifetime
cookie_path
cookie_domain
cookie_secure
cookie_httponly
Customization
return $monolog;
Traits
log
Logs a message
$app->log(sprintf("User '%s' registered.", $username));
session.test
Services
session
session.storage
Registering
$app->register(new Silex\Provider\SessionServiceProvider());
Services
monolog
(default: value of
sys_get_temp_dir())
Registering
$app->register(new Silex\Provider\MonologServiceProvider(),
array(
monolog.logfile' => __DIR__.'/dev.log',
));
Parameters
* session.storage.save_path
session.storage.options
Usage
$user = $app['session']->get('user'))
* - optional
swiftmailer.transport
(default: Swift_Transport_EsmtpTransport)
swiftmailer.transport.buffer
Parameters
swiftmailer.use_spool A boolean to specify whether or not
to use the memory spool.
(default: true)
swiftmailer.options
SMTP hostname.
(default: 'localhost')
swiftmailer.transport.authhandler
Authentication handler
used by the transport.
Try by default:
CRAM-MD5, login,
plaintext
Registering
username
SMTP username.
(default: empty string)
password
SMTP password.
(default: empty string)
$app->register(
new Silex\Provider\SwiftmailerServiceProvider()
);
encryption
SMTP encryption.
(default: null)
auth_mode
SMTP authentication
mode. (default: null)
mailer
mail
The mailer instance
$message = \Swift_Message::newInstance();
// ...
$app['mailer']->send($message);
Parameters
* translator.domains
Mapping of domains/locales/messages.
Contains the translation data for all
languages and domains
* locale
(default: en)
* locale_fallbacks
Services
translator
translator.loader
(default: ArrayLoader)
Instance of an implementation
of the translation
LoaderInterface
translator.message_selector
Instance of MessageSelector
Usage
Traits
Services
(default: en)
port
$app['swiftmailer.options'] = array(
'host' => 'host',
'port' => '25',
'username' => 'username',
'password' => 'password',
'encryption' => null,
auth_mode' => null
);
Registering
$app->register(new Silex\Provider\TranslationServiceProvider(),
array(
'locale_fallbacks' => array('en'),
));
Usage
Sends an email
$app->mail(\Swift_Message::newInstance()
->setSubject('[YourSite] Feedback')
->setFrom(array('noreply@yoursite.com'))
->setTo(array('feedback@yoursite.com'))
->setBody($request->get('message')));
$app['translator.domains'] = array(
'messages' => array(
'en' => array(
'hello' => 'Hello %name%',
'goodbye' => 'Goodbye %name%',
),
* - optional
),
);
$app->get('/{_locale}/{message}/{name}',
function ($message, $name) use ($app) {
return $app['translator']->trans(
$message,
array('%name%' => $name)
);
});
The above example will result in following routes:
/en/hello/igor will return Hello igor
/de/hello/igor will return Hallo igor
/fr/hello/igor will return Bonjour igor
/it/hello/igor will return Hello igor (fallback)
Parameters
* twig.path
* twig.templates
* twig.options
* twig.form.templates
render function
A render function is also registered to help
render another controller from a template:
$app['twig.path']
= array(__DIR__.'/../templates');
$app['twig.options'] = array(
'cache' => __DIR__.'/../var/cache/twig'
);
{{render(app.request.baseUrl ~ '/sidebar') }}
{# or if using the UrlGeneratorServiceProvider #}
{{render(url('sidebar')) }}
Services
twig
twig.loader
Traits
render
Registering
Traits
trans
transChoice
$app->register(new Silex\Provider\TwigServiceProvider(),
array(
'twig.path' => __DIR__.'/views',
));
Usage
$app->get('/hello/{name}', function ($name) use ($app) {
return $app['twig']->render('hello.twig', array(
'name' => $name,
));
});
Customization
You can configure the Twig environment before using it by
extending the twig service:
$app['twig'] = $app->share(
$app->extend('twig', function($twig, $app) {
$twig->addGlobal('pi', 3.14);
$twig->addFilter('levenshtein',
new \Twig_Filter_Function('levenshtein'));
return $twig;
}));
* - optional
Services
url_generator
Services
validator
Instance of Validator
validator.mapping.
class_metadata_factory
Registering
$app->register(
new Silex\Provider\UrlGeneratorServiceProvider()
);
Usage
$app->get('/', function () {
return 'welcome to the homepage';
})
->bind('home');
$app->get('/navigation', function () use ($app) {
return '<a href="'.
$app['url_generator']->generate('home').
'">Home</a>';
});
Registering
$app->register(new Silex\Provider\ValidatorServiceProvider());
{{ app.url_generator.generate('home') }}
Usage
Validating Values
{{ url('home') }}
{# generates the absolute url http://example.org/ #}
Traits
path
url
Generates a path
Generates an absolute URL
$app->path('home);
$app->url('home');
{{ path('home') }}
Validating Objects
if (count($errors) > 0) {
foreach ($errors as $error) {
echo $error->getPropertyPath().' '.
$error->getMessage()."\n";
}
}else {
echo 'The author is valid';
}
Translation
$app['translator.domains'] = array(
'validators' => array(
'fr' => array(
'This value should be a valid number.' =>
'Cette valeur doit tre un nombre.',
)
));
$form->handleRequest($req);
Registering
if ($form->isValid()) {
$data = $form->getData();
// do something with the data
// redirect somewhere
$app->register(new Silex\Provider\HttpCacheServiceProvider(),
array(
'http_cache.cache_dir' => __DIR__.'/cache/',
));
Parameters
form.secret
(default:
md5(__DIR__))
Services
form.factory
return $app->redirect('...');
Traits
form
form.csrf_provider Instance of an
implementation of the
(default:
DefaultCsrfProvider) CsrfProviderInterface
Usage
$app->get('/', function() {
return new Response('Foo', 200, array(
'Cache-Control' => 's-maxage=5',
));
});
$app->register(new FormServiceProvider());
Parameters
Usage
$app->match('/form', function (Request $req) use ($app) {
// default data for when the form is displayed the first time
$data = array(
'name' => 'Your name',
'email' => 'Your email',
);
$form = $app['form.factory']->createBuilder('form', $data)
->add('name')
->add('email')
->add('gender', 'choice', array(
'choices' => array(1 => 'male', 2 => 'female'),
'expanded' => true,
))
->getForm();
http_cache.cache_dir
* http_cache.options
The Symfony2 reverse proxy acts much like any other proxy
would, so whitelist it:
Request::setTrustedProxies(array('127.0.0.1'));
$app['http_cache']->run();
Services
http_cache
Instance of HttpCache
http_cache.esi
http_cache.store
Disabling ESI
$app->register(new Silex\Provider\HttpCacheServiceProvider(),
array(
'http_cache.cache_dir' => __DIR__.'/cache/',
'http_cache.esi' => null,
));
* - optional
Symfony 2.4+
Parameters
Parameters
fragment.path
(default:
/_fragment)
uri_signer.secret
$app->boot();
security.hide_user_not_found
(default: true)
Usage
// Current user
$token = $app['security']->getToken();
Services
security
security.
authentication_manager
Instance of
AuthenticationProviderManager,
responsible for authentication
security.access_manager
Instance of
AccessDecisionManager,
responsible for authorization
security.session_strategy
Registering
security.user_checker
$app->register(new
Silex\Provider\HttpFragmentServiceProvider()
);
security.last_error
security.encoder_factory
Services
fragment.handler
Instance of FragmentHandler
Usage
Using Twig for your templates:
security.encoder.digest
Registering
$app->register(new Silex\Provider\SecurityServiceProvider(),
array(
'security.firewalls' => // see below
));
* - optional
Traits
user
encodePassword
secure
$user = $app->user();
$encoded = $app->encodePassword($user, 'foo');
$app->get('/', function () {
// do something but only for admins
})->secure('ROLE_ADMIN');
secure
httponly
always_remember_me
Services
Registering
$app->register(new Silex\Provider\SecurityServiceProvider());
$app->register(new Silex\Provider\RememberMeServiceProvider());
$app['security.firewalls'] = array(
'my-firewall' => array(
'pattern' => '^/secure$',
'form' => true,
'logout' => true,
'remember_me' => array(
'key' => 'Choose_A_Unique_Random_Key',
'always_remember_me' => true,
),
'users' => array( /* ... */ ),
),
);
Symfony\Component\Serializer\Serializer
serializer.encoders
Symfony\Component\Serializer\
Encoder\JsonEncoder and
Symfony\Component\Serializer\
Encoder\XmlEncoder
serializer.normalizers Symfony\Component\Serializer\
Normalizer\CustomNormalizer and
Symfony\Component\Serializer\
Normalizer\GetSetMethodNormalizer
Registering
$app->register(new Silex\Provider\SerializerServiceProvider());
Usage
Options
$format = $app['request']->getRequestFormat();
if (!$page instanceof Page) {
$app->abort("No page found for id: $id");
}
key
name
lifetime
path
domain
Registering
$app->register(
new Silex\Provider\ServiceControllerServiceProvider());
Usage
/posts.json route will use a controller that is defined as a
service
use Silex\Application;
use Demo\Repository\PostRepository;
$app = new Application();
$app['posts.repository'] = $app->share(function() {
return new PostRepository;
});
$app->get('/posts.json', function() use ($app) {
return $app->json($app['posts.repository']->findAll());
});
Define the controller as a service:
$app['posts.controller'] = $app->share(function() use ($app) {
return new PostController($app['posts.repository']);
});
$app->get('/posts.json', "posts.controller:indexJsonAction");