Commit c61908b3 authored by Fabien Potencier's avatar Fabien Potencier

Merge remote branch 'igorw/docs'

* igorw/docs:
  [docs] note that twig is not included
  [docs] add Extension suffix to extension titles
  [docs] some documentation for the UrlGeneratorExtension
  [docs] better examples for the extensions chapter
  [docs] remove reference to symfony_bridges in TwigExtension docs
  [docs] move extension documentation to sub-directory
  [docs] remove _exts
  [docs] more on routes and twig
  [docs] initial documentation draft
parents 880729b1 17f96105
import sys, os
from sphinx.highlighting import lexers
from pygments.lexers.web import PhpLexer
sys.path.append(os.path.abspath('_exts'))
extensions = ['configurationblock']
master_doc = 'index'
highlight_language = 'php'
project = u'Silex'
copyright = u'2010 Fabien Potencier'
version = '0'
release = '0.0.0'
lexers['php'] = PhpLexer(startinline=True)
Contributing
============
We are open to contributions to the Silex code. If you find
a bug or want to contribute an extension, just follow these
steps.
* Fork `the Silex repository <https://github.com/fabpot/Silex>`_
on github.
* Make your feature addition or bug fix.
* Add tests for it. This is important so we don't break it in a future version unintentionally.
* Send a pull request. Bonus points for topic branches.
.. note::
Any code you contribute must be licensed under the MIT
License.
Extensions
==========
Silex provides a common interface for extensions. These
define services on the application.
Loading extensions
------------------
In order to load and use an extension, you must register it
on the application. ::
use Acme\DatabaseExtension;
$app = new Application();
$app->register(new DatabaseExtension());
You can also provide some parameters as a second argument.
::
$app->register(new DatabaseExtension(), array(
'database.dsn' => 'mysql:host=localhost;dbname=myapp',
'database.user' => 'root',
'database.password' => 'secret_root_password',
));
Included extensions
-------------------
There are a few extensions that you get out of the box.
All of these are within the ``Silex\Extension`` namespace.
* :doc:`MonologExtension <extensions/monolog>`
* :doc:`TwigExtension <extensions/twig>`
* :doc:`UrlGeneratorExtension <extensions/url_generator>`
Creating an extension
---------------------
Extensions must implement the ``Silex\ExtensionInterface``.
::
interface ExtensionInterface
{
function register(Application $app);
}
This is very straight forward, just create a new class that
implements the ``register`` method. In this method you must
define services on the application which then may make use
of other services and parameters.
Here is an example of such an extension::
namespace Acme;
use Silex\ExtensionInterface;
class HelloExtension implements ExtensionInterface
{
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;
return "Hello $name";
});
}
}
This class provides a ``hello`` service which is a protected
closure. It takes a name argument and will return
``hello.default_name`` if no name is given. If the default
is also missing, it will use an empty string.
You can now use this extension as follows::
use Acme\HelloExtension;
$app = new Application();
$app->register(new HelloExtension(), array(
'hello.default_name' => 'Igor',
));
$app->get('/hello', function() use ($app) {
$name = $app['request']->get('name');
return $app['hello']($name);
});
In this example we are getting the ``name`` parameter from the
query string, so the request path would have to be ``/hello?name=Fabien``.
Class loading
~~~~~~~~~~~~~
Extensions are great for tying in external libraries as you
can see by looking at the ``MonologExtension`` and
``TwigExtension``. If the library is decent and follows the
`PSR-0 Naming Standard <http://groups.google.com/group/php-standards/web/psr-0-final-proposal>`_
or the PEAR Naming Convention, it is possible to autoload
classes using the ``UniversalClassLoader``.
As described in the *Services* chapter, there is an
*autoloader* service that you can use for this.
Here is an example of how to use it (based on `Buzz <https://github.com/kriswallsmith/Buzz>`_)::
namespace Acme;
use Silex\ExtensionInterface;
class BuzzExtension implements ExtensionInterface
{
public function register(Application $app)
{
$app['buzz'] = $app->share(function() { ... });
if (isset($app['buzz.class_path'])) {
$app['autoloader']->registerNamespace('Buzz', $app['buzz.class_path']);
}
}
}
This allows you to simply provide the class path as an
option when registering the extension::
$app->register(new BuzzExtension(), array(
'buzz.class_path' => __DIR__.'/vendor/buzz/lib',
));
.. note::
For libraries that do not use PHP 5.3 namespaces you can use ``registerPrefix``
instead of ``registerNamespace``, which will use an underscore as directory
delimiter.
Silex
=====
.. toctree::
:maxdepth: 2
monolog
twig
url_generator
MonologExtension
================
The *MonologExtension* provides a default logging mechanism
through Jordi Boggiano's `Monolog <https://github.com/Seldaek/monolog>`_
library.
It will log requests and errors and allow you to add debug
logging to your application, so you don't have to use
``var_dump`` so much anymore. You can use the grown-up
version called ``tail -f``.
Parameters
----------
* **monolog.logfile**: File where logs are written to.
* **monolog.class_path** (optional): Path to where the
Monolog library is located.
* **monolog.level** (optional): Level of logging defaults
to ``DEBUG``. Must be one of ``Logger::DEBUG``, ``Logger::INFO``,
``Logger::WARNING``, ``Logger::ERROR``. ``DEBUG`` will log
everything, ``INFO`` will log everything except ``DEBUG``,
etc.
* **monolog.name** (optional): Name of the monolog channel,
defaults to ``myapp``.
Services
--------
* **monolog**: The monolog logger instance.
Example usage::
$app['monolog']->addDebug('Testing the Monolog logging.');
* **monolog.configure**: Protected closure that takes the
logger as an argument. You can override it if you do not
want the default behavior.
Registering
-----------
Make sure you place a copy of *Monolog* in the ``vendor/monolog``
directory.
::
use Silex\Extension\MonologExtension;
$app->register(new MonologExtension(), array(
'monolog.logfile' => __DIR__.'/development.log',
'monolog.class_path' => __DIR__.'/vendor/monolog/src',
));
TwigExtension
=============
The *TwigExtension* provides integration with the `Twig
<http://www.twig-project.org/>`_ template engine.
Parameters
----------
* **twig.path**: Path to the directory containing twig template
files.
* **twig.templates** (optional): If this option is provided
you don't have to provide a ``twig.path``. It is an
associative array of template names to template contents.
Use this if you want to define your templates inline.
* **twig.options** (optional): An associative array of twig
options. Check out the twig documentation for more information.
* **twig.class_path** (optional): Path to where the Twig
library is located.
Services
--------
* **twig**: The ``Twig_Environment`` instance, you will only
need to use this.
* **twig.configure**: Protected closure that takes the twig
environment as an argument. You can use it to add more
custom globals.
* **twig.loader**: The loader for twig templates which uses
the ``twig.path`` and the ``twig.templates`` options. You
can also replace the loader completely.
Registering
-----------
Make sure you place a copy of *Twig* in the ``vendor/twig``
directory.
::
use Silex\Extension\TwigExtension;
$app->register(new TwigExtension(), array(
'twig.path' => __DIR__.'/views',
'twig.class_path' => __DIR__.'/vendor/twig/lib',
));
.. note::
Twig is not compiled into the ``silex.phar`` file. You have to
add your own copy of Twig to your application.
Usage
-----
The Twig extension provides a ``twig`` service.
::
$app->get('/hello/{name}', function($name) use ($app) {
return $app['twig']->render('hello.twig', array(
'name' => $name,
));
});
This will render a file named ``views/hello.twig``.
It also registers the application as a global named
``app``. So you can access any services from within your
view. For example to access ``$app['request']->getHost()``,
just put this in your template:
.. code-block:: jinja
{{ app.request.host }}
UrlGeneratorExtension
=====================
The *UrlGeneratorExtension* provides a service for generating
URLs for named routes.
Parameters
----------
None.
Services
--------
* **url_generator**: An instance of
``Symfony\Component\Routing\Generator\UrlGenerator``, using the
``RouteCollection`` that is provided through the ``routes`` service.
It has a ``generate`` method, which takes the route name as an argument,
followed by an array of route parameters.
Registering
-----------
::
use Silex\Extension\UrlGeneratorExtension;
$app->register(new UrlGeneratorExtension());
Usage
-----
The UrlGenerator extension provides a ``url_generator`` service.
::
$app->get('/', function() {
return 'welcome to the homepage';
})
->bind('homepage');
$app->get('/hello/{name}', function($name) {
return "Hello $name!";
})
->bind('hello');
$app->get('/navigation', function() use ($app) {
return '<a href="'.$app['url_generator']->generate('homepage').'">Home</a>'.
' | '.
'<a href="'.$app['url_generator']->generate('hello', array('name' => 'Igor')).'">Hello Igor</a>';
});
Silex
=====
.. toctree::
:maxdepth: 2
intro
usage
services
extensions
internals
contributing
Internals
=========
This chapter will tell you a bit about how Silex works
internally.
Silex
-----
Application
~~~~~~~~~~~
The application is the main interface to Silex. It
implements Symfony2's ``HttpKernelInterface``, so you can
pass a ``Request`` to the ``handle`` method and it will
return a ``Response``.
It extends the ``Pimple`` service container, allowing
for flexibility on the outside as well as the inside. you
could replace any service, and you are also able to read
them.
The application makes strong use of the ``EventDispatcher``
to hook into the Symfony2 HttpKernel events. This allows
fetching the ``Request``, converting string responses into
``Response`` objects and handling Exceptions. We also use it
to dispatch some custom events like before/after filters and
errors.
Controller
~~~~~~~~~~
The Symfony2 ``Route`` is actually quite powerful. Routes
can be named, which allows for URL generation. They can
also have requirements for the variable parts. In order
to allow settings these through a nice interface the
``match`` method (which is used by ``get``, ``post``, etc.)
returns an instance of the ``Controller``, which wraps
a route.
ControllerCollection
~~~~~~~~~~~~~~~~~~~~
One of the goals of exposing the ``RouteCollection`` was
to make it mutable, so extensions could add stuff to it.
The challenge here is the fact that routes know nothing
about their name. The name only has meaning in context
of the ``RouteCollection`` and cannot be changed.
To solve this challenge we came up with a staging area
for routes. The ``ControllerCollection`` holds the
controllers until ``flush`` is called, at which point
the routes are added to the ``RouteCollection``. Also,
the controllers are frozen. This means that they can
no longer be modified and will throw an Exception if
you try to do so.
Unfortunately no good way for flushing implicitly
could be found, which is why flushing is now always
explicit. The Application will flush, but if you want
to read the ``ControllerCollection`` before the
request takes place, you will have to call flush
first.
The ``Application`` provides a shortcut ``flush``
method for flushing the ``ControllerCollection``.
Symfony2
--------
Following Symfony2 components are used by Silex:
* **ClassLoader**: For autoloading classes.
* **HttpFoundation**: For ``Request`` and ``Response``.
* **HttpKernel**: Because we need a heart.
* **Routing**: For matching defined routes.
* **EventDispatcher**: For hooking into the HttpKernel.
Introduction
============
Silex is a PHP microframework for PHP 5.3. It is built on the shoulders
of Symfony2 and Pimple and also inspired by sinatra.
A microframework provides the guts for building simple single-file apps.
Silex aims to be:
* *Concise*: Silex exposes a intuitive and concise API that is fun to use.
* *Extensible*: Silex has an extension system based around the Pimple
micro service-container that makes it even easier to tie in third party
libraries.
* *Testable*: Silex uses Symfony2's HttpKernel which abstracts request and
response. This makes it very easy to test apps and the framework itself.
It also respects the HTTP specification and encourages its proper use.
In a nutshell, you define controllers and map them to routes, all in one
step.
Let's go! ::
require_once __DIR__.'/silex.phar';
$app = new Silex\Application();
$app->get('/hello/{name}', function($name) {
return "Hello $name";
});
$app->run();
All that is needed to get access to the Framework is to include
``silex.phar``. This phar (PHP Archive) file will take care of the rest.
Next we define a route to ``/hello/{name}`` that matches for ``GET``
requests. When the route matches, the function is executed and the return
value is sent back to the client.
Finally, the app is run. It's really that easy!
Installation
------------
Installing Silex is as easy as it can get. Download the [`silex.phar`][2] file
and you're done!
License
-------
Silex is licensed under the MIT license.
.. code-block:: text
Copyright (c) 2010 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Services
========
Silex is not only a microframework. It is also a micro service container.
It does this by extending `Pimple <https://github.com/fabpot/Pimple>`_
which provides service goodness in just 44 NCLOC.
Dependency Injection
--------------------
.. note::
You can skip this if you already know what Dependency Injection is.
Dependency Injection is a design pattern where you pass dependencies
to services instead of creating them from within the service or
relying on globals. This generally leads to code that is decoupled,
re-usable, flexible and testable.
Here is an example::
class JsonUserPersister
{
private $basePath;
public function __construct($basePath)
{
$this->basePath = $basePath;
}
public function persist(User $user)
{
$data = $user->getAttributes();
$json = json_encode($data);
$filename = $this->basePath.'/'.$user->id.'.json';
file_put_contents($filename, $json, LOCK_EX);
}
}
In this simple example the dependency is the ``basePath`` property.
It is passed to the constructor. This means you can create several
independent instances with different base paths. Of course
dependencies do not have to be simple strings. More often they are
in fact other services.
Container
~~~~~~~~~
A DI or service container is responsible for creating and storing
services. It can recursively create dependencies of the requested
services and inject them. It does so lazily, which means a service
is only created when you actually need it.
Most containers are quite complex and are configured through XML
or YAML files.
Pimple is different.
Pimple
------
Pimple is probably the simplest service container out there. It
makes strong use of closures implements the ArrayAccess interface.
We will start off by creating a new instance of Pimple -- and
because ``Silex\Application`` extends ``Pimple`` all of this
applies to Silex as well. ::
$container = new Pimple();
or ::
use Silex\Application;
$app = new Application();
Parameters
~~~~~~~~~~
You can set parameters (which are usually strings) by setting
an array key on the container::
$app['some_parameter'] = 'value';
The array key can be anything, by convention periods are
used for namespacing. ::
$app['asset.host'] = 'http://cdn.mysite.com/';
Reading parameter values is possible with the same
syntax. ::
echo $app['some_parameter'];
Service definitions
~~~~~~~~~~~~~~~~~~~
Defining services is no different than defining parameters.
You just set an array key on the container to be a closure.
The difference is, when you retrieve the service, the
closure is executed. This allows for lazy service creation.
::
$app['some_service'] = function() {
return new Service();
};
And to retrieve the service, use::
$service = $app['some_service'];
Every time you call ``$app['some_service']``, a new instance
of the service is created.
Shared services
~~~~~~~~~~~~~~~
You may want to use the same instance of a service across all
of your code. In order to do that you can make a *shared*
service. ::
$app['some_service'] = $app->share(function() {
return new Service();
});
This will create the service on first invocation, and then
return the existing instance on any subsequent access.
Access container from closure
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In many cases you will want to access the service container
from within a service definition closure. For example when
fetching services the current service depends on.
Because of this, the container is passed to the closure as
an argument. ::
$app['some_service'] = function($app) {
return new Service($app['some_other_service'], $app['some_service.config']);
};
Here you can see an example of Dependency Injection.
``some_service`` depends on ``some_other_service`` and
takes ``some_service.config`` as configuration options.
The dependency is only created when ``some_service`` is
accessed, and it is possible to replace either of the
dependencies by simply overriding those definitions.
.. note::
This also works for shared services.
Protected closures
~~~~~~~~~~~~~~~~~~
Because the container sees closures as factories for
services, it will always execute them when reading them.
In some cases you will however want to store a closure
as a parameter, so that you can fetch it and execute it
yourself -- with your own arguments.
This is why Pimple allows you to protect your closures
from being executed, by using the ``protect`` method.
::
$app['closure_parameter'] = $app->protect(function($a, $b) {
return $a + $b;
});
// will not execute the closure
$add = $app['closure_parameter'];
// calling it now
echo $add(2, 3);
Note that protected closures do not get access to
the container.
Predefined services
-------------------
Silex defines a range of services which can be used
or replaced. You probably don't want to mess with most
of them.
* **request**: Contains the current request object,
which is an instance of ``Symfony\Component\HttpFoundation\Request``.
It gives you access to ``GET``, ``POST`` parameters
and lots more!
Example usage::
$id = $app['request']->get('id');
* **autoloader**: This service provides you with a
``Symfony\Component\ClassLoader\UniversalClassLoader``
that is already registered. You can register prefixes
and namespaces on it.
Example usage, autoloads Twig classes::
$app['autoloader']->registerPrefix('Twig_', $app['twig.class_path']);
* **routes**: The ``Symfony\Component\Routing\RouteCollection``
that is used internally. You can add, modify, read
routes.
* **controllers**: The ``Silex\ControllerCollection``
that is used internally. Check the *Internals*
chapter for more information.
* **dispatcher**: The ``Symfony\Component\EventDispatcher\EventDispatcher``
that is used internally. It is the core of the Symfony2
system and is used quite a bit by Silex.
* **resolver**: The ``Symfony\Component\HttpKernel\Controller\ControllerResolver``
that is used internally. It takes care of executing the
controller with the right arguments.
* **kernel**: The ``Symfony\Component\HttpKernel\HttpKernel``
that is used internally. The HttpKernel is the heart of
Symfony2, it takes a Request as input and returns a
Response as output.
.. note::
All of these Silex core services are shared.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment