Commit 95414749 authored by Fabien Potencier's avatar Fabien Potencier

merged mount branch

parents 9772adaf b81e687f
......@@ -12,7 +12,7 @@ controller definitions, call the ``run`` method on your application.
::
require __DIR__.'/silex.phar';
require_once __DIR__.'/silex.phar';
use Silex\Application;
......@@ -380,6 +380,48 @@ correctly, to prevent Cross-Site-Scripting attacks.
);
});
Reusing applications
--------------------
To make your applications reusable, return the ``$app`` variable instead of
calling the ``run()`` method::
// blog.php
require_once __DIR__.'/silex.phar';
$app = new Silex\Application();
// define your blog app
$app->get('/post/{id}', function ($id) { ... });
// return the app instance
return $app;
Running this application can now be done like this::
$app = require __DIR__.'/blog.php';
$app->run();
This pattern allows you to easily "mount" this application under any other
one::
$blog = require __DIR__.'/blog.php';
$app = new Silex\Application();
$app->mount('/blog', $blog);
// define your main app
$app->run();
Now, blog posts are available under the ``/blog/post/{id}`` route, along side
any other routes you might have defined.
If you mount many applications, you might want to avoid the overhead of
loading them all on each request by using the ``LazyApplication`` wrapper::
$blog = new LazyApplication(__DIR__.'/blog.php');
Console
-------
......
......@@ -217,7 +217,7 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
{
$this['dispatcher']->addListener(Events::onSilexError, function(GetResponseForErrorEvent $event) use ($callback) {
$exception = $event->getException();
$result = $callback->__invoke($exception);
$result = $callback($exception);
if (null !== $result) {
$event->setStringResponse($result);
......@@ -257,6 +257,34 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
return htmlspecialchars($text, ENT_COMPAT, 'UTF-8');
}
/**
* Mounts an application under the given route prefix.
*
* @param string $prefix The route prefix
* @param Application|\Closure $app An Application instance or a Closure that returns an Application instance
*/
public function mount($prefix, $app)
{
$mountHandler = function (Request $request, $prefix) use ($app) {
if (is_callable($app)) {
$app = $app();
}
foreach ($app['controllers']->all() as $controller) {
$controller->getRoute()->setPattern(rtrim($prefix, '/').$controller->getRoute()->getPattern());
}
return $app->handle($request);
};
$prefix = rtrim($prefix, '/');
$this
->match($prefix.'/{path}', $mountHandler)
->assert('path', '.*')
->value('prefix', $prefix);
}
/**
* Handles the request and deliver the response.
*
......
<?php
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Silex;
use Symfony\Component\HttpFoundation\Request;
/**
* A Lazy application wrapper.
*
* Acts as a closure, so it can be used as a lazy app
* factory for Silex\Application::mount().
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class LazyApplication
{
protected $appPath;
protected $app;
/**
* Constructor.
*
* The $app argument is the path to a Silex app file.
* This file must return a Silex application.
*
* @param string $app The absolute path to a Silex app file
*/
public function __construct($appPath)
{
$this->appPath = $appPath;
}
/**
* Returns the application.
*/
public function __invoke()
{
if (!$this->app) {
$this->app = require $this->appPath;
}
if (!$this->app instanceof Application) {
throw new \InvalidArgumentException('The provided path did not return a Silex\Application on inclusion.');
}
return $this->app;
}
}
......@@ -12,6 +12,9 @@
namespace Silex\Tests;
use Silex\Application;
use Silex\LazyApplication;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Functional test cases.
......@@ -39,4 +42,88 @@ class FunctionalTest extends \PHPUnit_Framework_TestCase
$this->assertInstanceOf('Symfony\Component\Routing\Route', $routes->get('homepage'));
$this->assertInstanceOf('Symfony\Component\Routing\Route', $routes->get('foo_abc'));
}
public function testMount()
{
$mounted = new Application();
$mounted->get('/{name}', function ($name) { return new Response($name); });
$app = new Application();
$app->mount('/hello', $mounted);
$response = $app->handle(Request::create('/hello/Silex'));
$this->assertEquals('Silex', $response->getContent());
}
public function testLazyMount()
{
$i = 0;
$mountedFactory = function () use (&$i) {
$i++;
$mounted = new Application();
$mounted->get('/{name}', function ($name) {
return new Response($name);
});
return $mounted;
};
$app = new Application();
$app->mount('/hello', $mountedFactory);
$app->get('/main', function() {
return new Response('main app');
});
$response = $app->handle(Request::create('/main'));
$this->assertEquals('main app', $response->getContent());
$this->assertEquals(0, $i);
$response = $app->handle(Request::create('/hello/Silex'));
$this->assertEquals('Silex', $response->getContent());
$this->assertEquals(1, $i);
}
public function testLazyMountWithAnExternalFile()
{
$tmp = sys_get_temp_dir().'/SilexLazyApp.php';
file_put_contents($tmp, <<<'EOF'
<?php
$app = new Silex\Application();
$app->get('/{name}', function ($name) { return new Symfony\Component\HttpFoundation\Response($name); });
return $app;
EOF
);
$mounted = new LazyApplication($tmp);
$app = new Application();
$app->mount('/hello', $mounted);
$response = $app->handle(Request::create('/hello/Silex'));
$this->assertEquals('Silex', $response->getContent());
unlink($tmp);
}
public function testLazyMountWithAnInvalidExternalFile()
{
$tmp = sys_get_temp_dir().'/SilexInvalidLazyApp.php';
file_put_contents($tmp, '');
$mounted = new LazyApplication($tmp);
$app = new Application();
$app->mount('/hello', $mounted);
try {
$app->handle(Request::create('/hello/Silex'));
$this->fail('Invalid LazyApplications should throw an exception.');
} catch (\InvalidArgumentException $e) {
}
}
}
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