Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • SensioLabs Professional services to help you with Symfony
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by SensioLabs
  1. Home
  2. Documentation
  3. Create Framework
  4. The Separation of Concerns
  • Documentation
  • Book
  • Reference
  • Bundles
  • Cloud

The Separation of Concerns

Edit this page

Warning: You are browsing the documentation for Symfony 3.0, which is no longer maintained.

Read the updated version of this page for Symfony 6.3 (the current stable version).

The Separation of Concerns

One down-side of our framework right now is that we need to copy and paste the code in front.php each time we create a new website. 40 lines of code is not that much, but it would be nice if we could wrap this code into a proper class. It would bring us better reusability and easier testing to name just a few benefits.

If you have a closer look at the code, front.php has one input, the Request and one output, the Response. Our framework class will follow this simple principle: the logic is about creating the Response associated with a Request.

Let's create our very own namespace for our framework: Simplex. Move the request handling logic into its own Simplex\\Framework class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// example.com/src/Simplex/Framework.php
namespace Simplex;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\HttpKernel\Controller\ControllerResolver;

class Framework
{
    protected $matcher;
    protected $resolver;

    public function __construct(UrlMatcher $matcher, ControllerResolver $resolver)
    {
        $this->matcher = $matcher;
        $this->resolver = $resolver;
    }

    public function handle(Request $request)
    {
        $this->matcher->getContext()->fromRequest($request);

        try {
            $request->attributes->add($this->matcher->match($request->getPathInfo()));

            $controller = $this->resolver->getController($request);
            $arguments = $this->resolver->getArguments($request, $controller);

            return call_user_func_array($controller, $arguments);
        } catch (ResourceNotFoundException $e) {
            return new Response('Not Found', 404);
        } catch (\Exception $e) {
            return new Response('An error occurred', 500);
        }
    }
}

And update example.com/web/front.php accordingly:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// example.com/web/front.php

// ...
$request = Request::createFromGlobals();
$routes = include __DIR__.'/../src/app.php';

$context = new Routing\RequestContext();
$matcher = new Routing\Matcher\UrlMatcher($routes, $context);
$resolver = new HttpKernel\Controller\ControllerResolver();

$framework = new Simplex\Framework($matcher, $resolver);
$response = $framework->handle($request);

$response->send();

To wrap up the refactoring, let's move everything but routes definition from example.com/src/app.php into yet another namespace: Calendar.

For the classes defined under the Simplex and Calendar namespaces to be autoloaded, update the composer.json file:

1
2
3
4
5
6
{
    "...": "...",
    "autoload": {
        "psr-4": { "": "src/" }
    }
}

Note

For the Composer autoloader to be updated, run composer dump-autoload.

Move the controller to Calendar\Controller\LeapYearController:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// example.com/src/Calendar/Controller/LeapYearController.php
namespace Calendar\Controller;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Calendar\Model\LeapYear;

class LeapYearController
{
    public function indexAction(Request $request, $year)
    {
        $leapyear = new LeapYear();
        if ($leapyear->isLeapYear($year)) {
            return new Response('Yep, this is a leap year!');
        }

        return new Response('Nope, this is not a leap year.');
    }
}

And move the is_leap_year() function to its own class too:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// example.com/src/Calendar/Model/LeapYear.php
namespace Calendar\Model;

class LeapYear
{
    public function isLeapYear($year = null)
    {
        if (null === $year) {
            $year = date('Y');
        }

        return 0 == $year % 400 || (0 == $year % 4 && 0 != $year % 100);
    }
}

Don't forget to update the example.com/src/app.php file accordingly:

1
2
3
4
$routes->add('leap_year', new Routing\Route('/is_leap_year/{year}', array(
    'year' => null,
    '_controller' => 'Calendar\\Controller\\LeapYearController::indexAction',
)));

To sum up, here is the new file layout:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
example.com
├── composer.json
├── composer.lock    
├── src
│   ├── app.php
│   └── Simplex
│       └── Framework.php
│   └── Calendar
│       └── Controller
│       │   └── LeapYearController.php
│       └── Model
│           └── LeapYear.php
├── vendor
│   └── autoload.php
└── web
    └── front.php

That's it! Our application has now four different layers and each of them has a well defined goal:

  • web/front.php: The front controller; the only exposed PHP code that makes the interface with the client (it gets the Request and sends the Response) and provides the boiler-plate code to initialize the framework and our application;
  • src/Simplex: The reusable framework code that abstracts the handling of incoming Requests (by the way, it makes your controllers/templates easily testable -- more about that later on);
  • src/Calendar: Our application specific code (the controllers and the model);
  • src/app.php: The application configuration/framework customization.
Previous page The HttpKernel Component: the Controller Resolver
Next page Unit Testing
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version
    We stand with Ukraine.
    Version:
    Put the code quality back at the heart of your project

    Put the code quality back at the heart of your project

    Measure & Improve Symfony Code Performance

    Measure & Improve Symfony Code Performance

    Symfony footer

    ↓ Our footer now uses the colors of the Ukrainian flag because Symfony stands with the people of Ukraine.

    Avatar of Toni Conca, a Symfony contributor

    Thanks Toni Conca (@tonic) for being a Symfony contributor

    1 commit • 2 lines changed

    View all contributors that help us make Symfony

    Become a Symfony contributor

    Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

    Learn how to contribute

    Symfony™ is a trademark of Symfony SAS. All rights reserved.

    • What is Symfony?

      • Symfony at a Glance
      • Symfony Components
      • Case Studies
      • Symfony Releases
      • Security Policy
      • Logo & Screenshots
      • Trademark & Licenses
      • symfony1 Legacy
    • Learn Symfony

      • Symfony Docs
      • Symfony Book
      • Reference
      • Bundles
      • Best Practices
      • Training
      • eLearning Platform
      • Certification
    • Screencasts

      • Learn Symfony
      • Learn PHP
      • Learn JavaScript
      • Learn Drupal
      • Learn RESTful APIs
    • Community

      • SymfonyConnect
      • Support
      • How to be Involved
      • Code of Conduct
      • Events & Meetups
      • Projects using Symfony
      • Downloads Stats
      • Contributors
      • Backers
    • Blog

      • Events & Meetups
      • A week of symfony
      • Case studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Documentation
      • Living on the edge
      • Releases
      • Security Advisories
      • SymfonyInsight
      • Twig
      • SensioLabs
    • Services

      • SensioLabs services
      • Train developers
      • Manage your project quality
      • Improve your project performance
      • Host Symfony projects

      Deployed on

    Follow Symfony

    Search by Meilisearch