Silex is a PHP micro-framework based on Symfony components and inspired by the Sinatra Ruby framework. In this article, we are going to get started with the framework and see the how it fits our needs.
Key Takeaways
- Silex is a PHP micro-framework based on Symfony components and inspired by the Sinatra Ruby framework. It is recommended to install Silex through composer which also allows the use of Twig as a template engine.
- Silex allows a flexible organization of the framework and facilitates routing by mapping URL patterns to controller functions. Features like routing parameters, named routes, and the ability to group controllers make handling requests more efficient.
- Silex provides the concept of ‘providers’ which are small classes that tie a component to the Silex application. These can be registered to the application instance and can be created by implementing the Silex\ServiceProviderInterface and defining the register method.
- As of June 2018, Silex is no longer actively maintained and it is recommended to switch to Symfony for future projects. While Silex is suitable for smaller, simpler projects, Symfony, being a full-stack framework, is more suitable for large-scale projects and offers a larger community and more extensive documentation.
Installation
The best and recommended way to install Silex is through composer:
// composer.json
{
"require": {
"silex/silex": "1.3.*@dev",
"twig/twig": "1.17.*@dev"
},
"require-dev": {
"symfony/var-dumper": "dev-master"
}
}
Run composer update --dev
to load the dependencies and generate the autoloader. We also required twig
because we want to use it as our template engine, and the new var-dumper
from Symfony as a development dependency – read more about it here.
Creating a Folder Structure
One of the things I like about Silex is that it gives you a bare bones framework that you can organize in any way you want.
|-app/
|----config/
|-resources/
|----views/
|----logs/
|-src/
|----MyApp/
|-public/
|----index.php
|----.htaccess
|-vendor/
|-composer.json
For instance, I don’t like my root application folder to be called web
, I prefer the normal public
folder. The src
directory is where we put our application specific code while the other folders are rather self explanatory.
Our public/index.php
file will create a new Silex\Application
which is our app container instance, and this is where we are going to wire the components.
// public/index.php
<?php
require_once __DIR__ . '/../vendor/autoload.php';
$app = new Silex\Application();
$app->run();
Now if you hit your root application URL, you should see a page not found error. You can turn on debugging by setting the debug
mode in the container to true
.
If you are having problems accessing your routes, make sure that your server document root is pointing to the public folder. You can check the doc for more info about configuring your webserver.
// public/index.php
//...
$app['debug'] = true;
$app->run();
Now if we try again we get a descriptive NotFoundHttpException
, because we didn’t register any routes.
Routing
Registering a route is straightforward, you simply map a URL pattern to a controller function.
// public/index.php
$app->get('/', function(){
return "Hello world";
});
We can handle get
, post
, put
and delete
, or we can use the match
method to handle any request method. The handle function must return either a string or a Symfony\Component\HttpFoundation\Response
instance.
// public/index.php
$app->get('/', function(){
return new Symfony\Component\HttpFoundation\Response("Hello world");
});
Routing Parameters
$app->get("/users/{id}", function($id){
return "User - {$id}";
});
We can add multiple parameters to the URL pattern, the only convention is that the name of the URL pattern parameter must match the name used in the function, or you’ll get a RuntimeException
. You can also specify a URL pattern using the assert
method, and a default value using the value
method.
$app->get("/users/{id}", function($id){
return "User - {$id}";
})
->value("id", 0) //set a default value
->assert("id", "\d+"); // make sure the id is numeric
One of my favorite route methods is convert
: it allows us to intercept the request and change the parameter value before passing it to the callback function.
$app->get("/users/{user}", function($user){
// return the user profile
return "User {$user}";
})->convert("user", function($id){
$userRepo = new User();
$user = $userRepo->find($id);
if(!$user){
return new Response("User #{$id} not found.", 404);
}
return $user;
});
In this example, the convert
method takes a user id, looks up the database and returns the user. A 404 response is returned if the user is not found.
If you are a Laravel fan, you are used to filters like auth
, csrf
and guest
. In Silex, however, you can provide a callback function to behave like a filter.
$app->get("/users/{user}", function($user){
// return the user profile
return "User {$user}";
})->before(function($request, $app){
// redirect if the user is not logged in
})
->after(function($request, $response){
// log request events
})
->finish(function(){
// log request event
});
Similarly, you can use the after
and finish
methods. But keep in mind that the finish
method doesn’t have access to the request
and response
because the response is already sent to the user.
Named Routes
When dealing with multiple routes it makes sense to name them descriptively. This can be helpful when updating the URL format or generating template links.
$app->get("/users/list", function(Silex\Application $app){
return "List of users";
})->bind('users');
If you are using the URL Generator Service Provider, you can generate a link directly to the route.
<a href="{{ app.url_generator.generate('users') }}">Users</a>
Controllers
In real world applications, we don’t use closures for routing, but rather create separate controller classes to handle the requests.
$app->get("/", "MyApp\Controller\HomeController::index");
Grouping Controllers
One of the main benefits of using class controllers is the ability to group them. When creating a RESTful API, the URL’s will be something like this:
/users
/users/id
[PUT, DELETE]/users/id/edit
A really clean way to deal with this is to group the controllers into something called controller providers. Our User
controller provider must implement the ControllerProviderInterface
and define the connect
method.
// src/MyApp/Controller/Provider/User.php
class User implements ControllerProviderInterface{
public function connect(Application $app)
{
$users = $app["controllers_factory"];
$users->get("/", "MyApp\\Controller\\UserController::index");
$users->post("/", "MyApp\\Controller\\UserController::store");
$users->get("/{id}", "MyApp\\Controller\\UserController::show");
$users->get("/edit/{id}", "MyApp\\Controller\\UserController::edit");
$users->put("/{id}", "MyApp\\Controller\\UserController::update");
$users->delete("/{id}", "MyApp\\Controller\\UserController::destroy");
return $users;
}
}
The $app['controllers_factory']
returns a new Silex\ControllerCollection
which holds our routing collection. The UserController
will handle the registered requests.
// src/MyApp/Controller/UserController.php
class UserController{
public function index(){
// show the list of users
}
public function edit($id){
// show edit form
}
public function show($id){
// show the user #id
}
public function store(){
// create a new user, using POST method
}
public function update($id){
// update the user #id, using PUT method
}
public function destroy($id){
// delete the user #id, using DELETE method
}
}
The only remaining part is to attach our controller collection to our application. When working with routes, I prefer the Laravel approach of registering them inside a separate file and including them.
// app/routes.php
$app->mount("/users", new \MyApp\Controller\Provider\User());
The mount
method takes the prefix and our User
provider class as parameters.
Additionally, one of the benefits of using controller collections is the ability to use the before
, after
and finish
filters without having to call them on every route.
// src/MyApp/Controller/Provider/User.php
class User implements ControllerProviderInterface{
public function connect(Application $app)
{
//...
$users->before(function(){
// check for something here
});
}
}
Providers
We mentioned this term before but it simply stands for a small class that ties a component to the Silex application. There is a list of pre-included providers and, to use one, you simply register it to the application instance.
// app/providers.php
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__.'/../views',
));
Of course this should be done after requiring Twig inside your composer and updating dependencies. Now if you visit the TwigServiceProvider::register
method you can see that we can access the Twig_Environment
from the container.
$app->get(function(){
return $app['twig']->render('home.twig');
});
To create your own provider for Silex, you need to implement the Silex\ServiceProviderInterface
and define the register
method. You can read more in the doc.
Conclusion
Silex is a small and fast framework for Symfony fans. This introduction is aimed at getting you on board and trying the framework. We didn’t cover everything but hopefully I can make you exited to give it a go. If you have any questions or opinions let me know in the comments!
Frequently Asked Questions about Silex and Symfony Micro-Framework
What is the main difference between Silex and Symfony?
Silex and Symfony are both PHP frameworks, but they serve different purposes. Symfony is a full-stack framework, meaning it provides a complete structure for building web applications. It includes a variety of components and tools for tasks like form handling, database abstraction, and more. On the other hand, Silex is a micro-framework. It’s lightweight and minimalistic, providing only the bare essentials for building web applications. This makes Silex more suitable for smaller, simpler projects where a full-stack framework like Symfony might be overkill.
Is Silex still maintained?
As of June 2018, Silex is no longer actively maintained. The Silex project has officially ended, and it’s recommended to switch to Symfony for future projects. However, existing Silex applications will continue to work, and you can still find the source code and documentation online if you need to maintain an existing Silex application.
How can I migrate from Silex to Symfony?
Migrating from Silex to Symfony involves several steps. First, you’ll need to replace Silex’s service providers with Symfony’s services. Then, you’ll need to update your routes to use Symfony’s routing system. Finally, you’ll need to update your controllers to use Symfony’s controller system. This can be a complex process, but there are resources available online to guide you through it.
What are the advantages of using Symfony over Silex?
Symfony offers several advantages over Silex. It’s a full-stack framework, so it provides a complete set of tools for building web applications. This includes components for tasks like form handling, database abstraction, and more. Symfony also has a larger community and more extensive documentation, making it easier to find help and resources. Finally, Symfony is actively maintained and regularly updated, ensuring that it stays up-to-date with the latest web development trends and technologies.
Can I use Silex for large-scale projects?
While it’s technically possible to use Silex for large-scale projects, it’s not recommended. Silex is a micro-framework, meaning it’s designed for small, simple projects. It provides only the bare essentials for building web applications, so you’ll need to add your own components and tools for more complex tasks. For large-scale projects, a full-stack framework like Symfony is usually a better choice.
How can I start a new project with Symfony?
Starting a new project with Symfony is straightforward. First, you’ll need to install Symfony on your development machine. Then, you can use Symfony’s command-line tool to create a new project. Once your project is set up, you can start adding routes, controllers, and views to build your application.
What are some good resources for learning Symfony?
There are many resources available for learning Symfony. The official Symfony documentation is a great place to start. It provides a comprehensive guide to the framework, with tutorials and examples for all of its components. There are also many online tutorials, video courses, and books available.
Can I use Symfony for small-scale projects?
Yes, you can use Symfony for small-scale projects. While Symfony is a full-stack framework, it’s also flexible and modular. You can choose to use only the components you need, making it suitable for both large and small projects.
How does Symfony handle database abstraction?
Symfony uses Doctrine, a powerful Object-Relational Mapping (ORM) library, for database abstraction. Doctrine allows you to work with databases in a way that’s more natural to object-oriented programming, abstracting the underlying SQL. It supports a wide range of database systems, including MySQL, PostgreSQL, SQLite, and more.
What is the future of Symfony?
Symfony continues to be actively developed and maintained. The Symfony team regularly releases new versions with improvements and new features. The community around Symfony is also very active, contributing to the framework and creating third-party bundles. With its robust features and active community, Symfony is likely to remain a popular choice for PHP web development for the foreseeable future.
Younes is a freelance web developer, technical writer and a blogger from Morocco. He's worked with JAVA, J2EE, JavaScript, etc., but his language of choice is PHP. You can learn more about him on his website.