Getting started: Setup, creating pages, routing, controllers, templates, config, components, bundles, reference.
Symfony Flex: It's not a symfony version it's a tool that replaces symfony installer and symfony standar edition.Automates the most common tasks of Symfony applications, like installing and removing bundles and other Composer dependencies. Symfony Flex works for Symfony 3.3 and higher. Starting from Symfony 4.0, Flex should be used by default, but it is still optional.
- alias system: shortcuts to packages (symfony.sh) - recipies can modify: symfony.lock (managed by flex) , new config files, add bundle(plugin system, config/bundles.php ), create files, modify gitignore file. same to remove
MIT license: Lo único que exige es que los derechos de autor sean incluidos en todas las copias o posibles porciones del software Vendor, bundle, bridge**:
symfony-project/ ├─ config/ ├─ public/ ├─ src/ ├─ tests/ ├─ var/ └─ vendor/
Symfony has two main parts:
The HttpFoundation component defines an object-oriented layer for the HTTP specification.
Request is represented by some global variables ($_GET
, $_POST
, $_FILES
, $_COOKIE
, $_SESSION
, ...) and the Response is generated by some functions (echo
, header()
, setcookie()
, ...).
The Symfony HttpFoundation component replaces these default PHP global variables and functions by an object-oriented layer.
The HttpKernel component provides a structured process for converting a Request
into a Response
by making use of the EventDispatcher component
Most of the things that happen between the request and the response in Symfony are events.
#app_dev.php $kernel = new AppKernel('dev', true); // AppKernel register Bundles //most important line in symfony => $response = $kernel->handle($request);
Provides a structured process for converting a Request
into a Response
by making use of the EventDispatcher component
In Symfony applications everything is already configured and ready to use with Controllers and Events and Event Listeners.
HttpKernel::handle()
calls ArgumentResolverInterface::getArguments() that returns the array of arguments that should be passed to that controllerHttpKernel::handle()
executes the controller.Response
value into a Response
, ex: Sensio => @Template, fos => FOSRestBUndleResponse
object just before it is sent, ex: WebDebugToolBarListner injects some javascriptYou can extend AbstractController to get access to some helper methods. get(service), generateurl, forward, addflash, redirect, isGranted, renderView, createForm, getUser, Request object, Session object
All the examples shown in this article use the same KernelEvents::EXCEPTION
.
LISTENER
// src/EventListener/ExceptionListener.php class ExceptionListener { public function onKernelException(GetResponseForExceptionEvent $event) { // You get the exception object from the received event $exception = $event->getException(); //... // sends the modified response object to the event $event->setResponse($response); }
# config/services.yaml services: App\EventListener\ExceptionListener: tags: - { name: kernel.event_listener, event: kernel.exception }
logic to decide which method to execute:
method
attribute defined in a tag in kernel.event_listener
on
+ "camel-cased event name"__invoke()
magic methodSUBSCRIBER
// src/EventSubscriber/ExceptionSubscriber.php class ExceptionSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents() { // return the subscribed events, their methods and priorities return array( KernelEvents::EXCEPTION => array( array('processException', 10), array('logException', 0), array('notifyException', -10), ) ); } public function processException(GetResponseForExceptionEvent $event) { // ... } //... }
Service is an object. Container is an array that holds service objects. Services need an order to be created, and have to be created when needed.
// define services with Definition Object of Symfony Container new YamlFileLoader($contaier, new FileLocator) // dumper $container->compile(); $dumper = new PhpDumper($container); file_put_contents('/directory/cachedContianer', $dumper->dump()); $cachedContainer = '/directory/'; $container->compile();
Service == Object The container allows you to centralize the way objects are constructed, better architecture. Services are created if needed and just once.
show avialable public services
php bin/console debug:autowiring
full list
php bin/console debug:container
# config/services.yaml services: # default configuration for services in *this* file _defaults: autowire: true # Automatically injects dependencies in your services. autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. public: false # Allows optimizing the container by removing unused services; this also means # fetching services directly from the container via $container->get() won't work. # The best practice is to be explicit about your dependencies anyway. # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name App\: resource: '../src/*' exclude: '../src/{Entity,Migrations,Tests,Kernel.php}' # ...
Inject Services in constructors => Autowiring
# explicitly configure the service App\Updates\SiteUpdateManager: arguments: $adminEmail: 'manager@example.com' # If you need to specify the service with a common name # explicitly configure the service App\Service\MessageGenerator: arguments: $logger: '@monolog.logger.request' You can bind arguments by name or type _defaults: bind: $adminEmail: 'manager@example.com' Psr\Log\LoggerInterface: '@monolog.logger.request' Psr\Log\LoggerInterface $requestLogger: '@monolog.logger.request'
If a service needs a lots of container parameters you can inject ParameterBagInterface and:
$sender = $this->param_bag_inter->get('mailer_sender');
autoconfigure: ¿?¿?¿?¿?¿? apply certain configuration to your services, based on your service's class. This is mostly used to auto-tag your services.
importing services from folder
App\: resource: '../src/*'
if public: $logger = $this->container->get('logger'); best practice: private and autowiring
Mechanism used by the di component to flag services that require special processing.
To register a service in symfony/bundle in some special way.
Ex: Services tagged with the twig.extension
tag are collected during the initialization of TwigBundle and added to Twig as extensions.
# Instead of dealing with 3 services you deal with 1 depending on some config. services: app.mysql_lock: # ... app.postgresql_lock: # ... app.sqlite_lock: # ... app.lock: tags: - { name: auto_alias, format: "app.%database_type%_lock" }
routing.loader
#: register a custom ser vice that loads routes services: App\Routing\CustomLoader: tags: [routing.loader]
Tags on their own don't actually alter the functionality of your services in any way. Ex: Create a "transport chain" for swiftmailer => try several ways of transporting the message until one succeeds. 1 - Define the tranportChain class: With and addTransport(\Swift_transport $transport) function 2 - Define that class as a servie 3 - Make that several of the swift_transport classes be instantiated and added to the chain automatically using the addTransport method. Ex, you may add the following transports as services:
services: Swift_SmtpTransport: arguments: ['%mailer_host%'] tags: ['app.mail_transport'] Swift_SendmailTransport: tags: ['app.mail_transport']
4 - Use the compiler pass to ask the container for any services with the app.mail_transport tag:
class MailTransportPass implements CompilerPassInterface { public function process(ContaierBuilder $container){ //... foreach ($taggedServices as $id => $tags) { // add the transport service to the TransportChain service $definition->addMethodCall('addTransport', [new Reference($id)]); } } }
5 - Register pass with the Container
class Kernel extends BaseKernel { // ... protected function build(ContainerBuilder $container) { $container->addCompilerPass(new MailTransportPass()); } }
Way of letting you interact with the service and parameter definitions before they have been compiled into the DIC. Steps:
Why is useful?
Compiler passes are registered in the build()
method of the application kernel:
Register compiler pass
class Kernel extends BaseKernel { use MicroKernelTrait; // ... protected function build(ContainerBuilder $container): void { $container->addCompilerPass(new CustomPass()); } }
Work with tagged services:
class Kernel extends BaseKernel implements CompilerPassInterface { use MicroKernelTrait; public function process(ContainerBuilder $container) { // in this method you can manipulate the service container: // for example, changing some container service: $container->getDefinition('app.some_private_service')->setPublic(true); // or processing tagged services: foreach ($container->findTaggedServiceIds('some_tag') as $id => $tags) { // ... } } }
class NewsletterManagerStaticFactory { public static function createNewsletterManager() { $newsletterManager = new NewsletterManager(); // ... return $newsletterManager; } }
services: # ... App\Email\NewsletterManager: # call the static method factory: ['App\Email\NewsletterManagerStaticFactory', createNewsletterManager] arguments: ['@templating']
//SIMPLE ex use Symfony\Component\Routing\Annotation\Route; /* * @Route("/blog", name="blog_list") * The feature to localize routes was introduced in Symfony 4.1. * @Route({ * "nl": "/over-ons", * "en": "/about-us" * }, name="about_us") * */ // Wildcard with default value /** * @Route("/blog/{page}", name="blog_list", requirements={"page"="\d+"}) */ public function list($page = 1) {} // ADVANCED ex /* {} Advanced example * @Route( * "/articles/{_locale}/{year}/{slug}.{_format}", * defaults={"_format": "html"}, * requirements={ * "_locale": "en|fr", * "_format": "html|rss", * "year": "\d+" * } * */ // ROUTE MATCHING conditions -The expression syntax: Evaluates expressions from strings passing objects, functions, arrays... /** * @Route( * "/contact", * name="contact", * condition="context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'" * ) * * expressions can also include config parameters * condition: "request.headers.get('User-Agent') matches '%app.allowed_browsers%'" */ // ADD Http method requirement // @Route("/api/posts/{id}", methods={"GET","HEAD"}) // Generate route from a controller $url = $this->generateUrl( 'blog_show', ['slug' => 'my-blog-post')] );
From other place use use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
The host that's used when generating an absolute URL is automatically detected using the current Request
object.
JMSI18nRoutingBundl or BeSimpleI18nRoutingBundle
How to work with the user's locale
The locale of the current user is stored in the request and is accessible via the Request
object
To set the user's locale, you may want to create a custom event listener so that it's set before any other parts of the system (i.e. the translator) need it.
Since you can store the locale of the user in the session, it may be tempting to use the same URL to display a resource in different languages based on the user's locale.
Unfortunately, this violates a fundamental rule of the Web: that a particular URL returns the same resource regardless of the user
A better policy is to include the locale in the URL.
_locale
parameter
if a user visits the URI /fr/contact
, the locale fr
will automatically be set as the locale for the current request.
default locale
framework:
default_locale: en
Twig defines three types of special syntax:
{{ ... }}
"Says something"
{% ... %}
"Does something"
{# ... #}
"Comment something":
{{ title|upper }} filters
{{ dump(user) }} functions
{% set foo = 'bar' %} tags
Variables
{{ foo.bar }}
{{ foo['bar'] }}
Twig is fast because each template is compiled to a native PHP class and cached. But don't worry: this happens automatically and doesn't require you to do anything.
{% extends 'base.html.twig' %} {% block title %}My cool blog posts{% endblock %} {% block body %} {% for entry in blog_entries %} <h2>{{ entry.title }}</h2> <p>{{ entry.body }}</p> {% endfor %} {{ parent() }} {% endblock %}
When generating HTML from templates, there's always a risk that a variable will include characters that affect the resulting HTML. There are two approaches: manually escaping each variable or automatically escaping everything by default.
Twig performs automatic "output escaping" when rendering any content in order to protect you from Cross Site Scripting (XSS) attacks.
{{ description }}
{{ description|raw }}
Macros are comparable with functions in regular programming languages. They are useful to reuse often used HTML fragments to not repeat yourself.
{% macro input(name, value = "", type = "text", size = 20) %} <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}" /> {% endmacro %}
{% set greeting = 'Hello ' %} {% set name = 'Fabien' %} {{ greeting ~ name|lower }} {# Hello fabien #}
templates/
to render/extend templates/blog/index.html.twig
, you'll use the blog/index.html.twig
path
<p>Username: {{ app.user.username }}</p> {% if app.debug %} <p>Request method: {{ app.request.method }}</p> <p>Application Environment: {{ app.environment }}</p> {% endif %}
#config/packages/twig.yaml twig:
# ... globals: ga_tracking: UA-xxxxx-x
{% for article in articles %} {{ include('article/article_details.html.twig', { 'article': article }) }} {% endfor %}
<a href="{{ path('article_show', {'slug': article.slug}) }}"> //- Link to asset <img src="{{ asset('images/logo.png') }}" alt="Symfony!" /> <link href="{{ asset('css/blog.css') }}" rel="stylesheet" />
<div id="sidebar"> {{ render(controller( 'App\\Controller\\ArticleController::recentArticles', { 'max': 3 } )) }} </div>
dump($translator->trans('Hello World'));
composer require symfony/translation
$translated = $translator->trans('Symfony is great');
# translations/messages.fr.yaml Symfony is great: J'aime Symfony
{{ message|trans({'%name%': 'Fabien'}, 'app') }}
String interpolation (#{expression}
) allows any valid expression to appear within a double-quoted string.
{{ "foo #{bar} baz" }} {{ "foo #{1 + 2} baz" }}
composer require symfony/form
We have an Data class.
$task = new Task(sdf,sadf); $form = $this->createFormBuilder($task) ->add('task', TextType::class) ->add('dueDate', DateType::class) ->add('save', SubmitType::class, array('label' => 'Create Task')) ->getForm(); return $this->render('default/new.html.twig', array( 'form' => $form->createView(), ));
{# templates/default/new.html.twig #} {{ form_start(form) }} {{ form_widget(form) }} {{ form_end(form) }}
$form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $task = $form->getData(); ///...... }
composer require symfony/validator
/**
* @Assert\NotBlank
* @Assert\Type("\DateTime")
*/
Text Fields, Choice Fields, Date Fields, group fields, hidden fields, button fields, other fields, custom fields
class TaskType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('task') ->add('dueDate', null, array('widget' => 'single_text')) ->add('save', SubmitType::class) ; } } $form = $this->createForm(TaskType::class, $task);
it's generally a good idea to explicitly specify the data_class
option
public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'data_class' => Task::class, )); }
When mapping forms to objects, all fields are mapped. If you wanat a field not to be mapped:
$builder ->add('task') ->add('dueDate') ->add('agreeTerms', CheckboxType::class, array('mapped' => false)) ->add('save', SubmitType::class) ;
When building forms, keep in mind that the first goal of a form is to translate data from an object (Task
) to an HTML form so that the user can modify that data. The second goal of a form is to take the data submitted by the user and to re-apply it to the object.
1) Pre-populaing the From (pre-set-data, post-set-data) 2) Submiting a form (pre-submit, submit, post-submit) Register event listeners or event subscribers
Modify field types, 2 use cases:
validator
service => form validation# config/packages/framework.yaml framework: validation: - enabled: true - enable_annotations: true
A php object that makes an assertive statement. Can be applied in php object property, forms, getters
//CONSTRAINT // src/Validator/Constraints/ContainsAlphanumeric.php namespace App\Validator\Constraints; use Symfony\Component\Validator\Constraint; /** * @Annotation */ class ContainsAlphanumeric extends Constraint { public $message = 'The string "{{ string }}" contains an illegal character: it can only contain letters or numbers.'; } //------ // CONSTRAINT VALIDATOR // src/Validator/Constraints/ContainsAlphanumericValidator.php namespace App\Validator\Constraints; class ContainsAlphanumericValidator extends ConstraintValidator { public function validate($value, Constraint $constraint) { if (null === $value || '' === $value) { return; } if (!is_string($value)) { throw new UnexpectedValueException($value, 'string'); } if (!preg_match('/^[a-zA-Z0-9]+$/', $value, $matches)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ string }}', $value) ->addViolation(); } } }
class ProtocolClass extends Constraint public function getTargets() { return self::CLASS_CONSTRAINT; } } // so you can use the entire protocol class class ProtocolClassValidator extends ConstraintValidator { public function validate($protocol, Constraint $constraint) { if ($protocol->getFoo() != $protocol->getBar()) { $this->context->buildViolation($constraint->message) ->atPath('foo') ->addViolation(); } } } // Apply it /** * @AcmeAssert\ContainsAlphanumeric */ class AcmeEntity { }
/** * @Assert\Callback */ public function validate(ExecutionContextInterface $context, $payload) { if (in_array($this->getFirstName(), $fakeNames)) { $context->buildViolation('This name sounds fake!') ->atPath('firstName') ->addViolation(); } }
If you want to execute a method out of the class you can use an static method public static function validate($object, ExecutionContextInterface $context, $payload)
/** * @Assert\Callback({"Acme\Validator", "validate"}) */ class Author { }
The Callback constraint does not support global callback functions If you want to validate a scalar or array you need the validator service.
In a class with this, will be 3 groups: The class (ex: User), the default, the registration. Default: All the constrainst that don't belong to any group If you use the default it will apply the constraint of the embedded objects.
$form = $this->createFormBuilder($user, array( 'validation_groups' => array('registration'), ))->add(...); // ---- or public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'validation_groups' => array('registration'), )); }
/** * @Assert\GroupSequence({"User", "Strict"}) */ class User implements UserInterface
Using GroupSequence make Defautl != User => Now the default will reference the group sequence. If you use it it will get an infinite recursion.
public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'validation_groups' => new GroupSequence(['First', 'Second']), ]); }
/** * @Assert\GroupSequenceProvider */ class User implements GroupSequenceProviderInterface { /** * @Assert\CardScheme( * schemes={"VISA"}, * groups={"Premium"}, * ) */ private $creditCard; public function getGroupSequence() { // if user fail => resto not validated return array('User', 'Premium', 'Api'); // if user fail => premium validated but api dont return array(array('User', 'Premium'), 'Api'); } }
Security in symfony
Firewall: Authentication system.
# config/packages/security.yaml security: firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: anonymous: ~
Only one firewall is active on each request. Symfony matches the first one with pattern key.
If you go to the homepage right now, you will have access and you'll see that you're "authenticated" as anon.
. Don't be fooled by the "Yes" next to Authenticated. The firewall verified that it does not know your identity, and so, you are anonymous:
Authentication in Symfony can feel a bit "magic" at first because, instead of building a route & controller to handle login, you'll activate an authentication provider: some code that runs automatically before your controller is called. Symfony has several built-in authentication providers (form_login, http_basic, json_login, ldap...). If your use-case matches one of these exactly, great! But, in most cases - including a login form - we recommend building a Guard Authenticator
A Guard authenticator is a class that gives you complete control over your authentication process.
Create a Authentication System with Guard (api, login form, sso system)
Custom Authentication System with Guard (API token Example)
1 - Create a User Class and add php bin/console make:user
/** * @ORM\Column(type="string", unique=true) */ private $apiToken;
2- Create the authenticator class
To create a custom authentication system, create a class and make it implement AuthenticatorInterface. Or, extend the simpler AbstractGuardAuthenticator.
3 - Configure the authenticator
firewalls: # ... main: anonymous: ~ logout: ~ guard: authenticators: - App\Security\TokenAuthenticator
Users can now log in to your app. Great! Now, you need to learn how to deny access and work with the User object.
The process of authorization has two different sides: 1 - A user receives a ROLE 2 - RESOURCES (urls, controllers) filter by ROLE
When a user logs in, Symfony calls the getRoles()
method on your User
object to determine which roles this user has. In the User
class that we generated earlier, the roles are an array that's stored in the database, and every user is always given at least one role: ROLE_USER
:
security: access_control: # require ROLE_ADMIN for /admin* - { path: ^/admin, roles: ROLE_ADMIN }
$this->denyAccessUnlessGranted('ROLE_ADMIN');
With SensioFrameworkExtraBundle
/** * @IsGranted("ROLE_ADMIN") */ class AdminController extends AbstractController { /** * @IsGranted("ROLE_ADMIN") */ public function adminDashboard() {} }
if ($this->security->isGranted('ROLE_SALES_ADMIN')) { $salesData['top_secret_numbers'] = rand(); }
2 options:
After authentication, the User
object of the current user can be accessed via the getUser()
shortcut:
controller: $this->getUser(); service: $user = $this->security->getUser(); template: {{ app.user.email }}
firewalls: main: # ... logout: path: app_logout
security: role_hierarchy: ROLE_ADMIN: ROLE_USER ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
To authorizate to access to a resource.
Voters are called when isGranted() is called on symfony's authorization checker or denyAccessUnlessGranted in a controller (that uses auth checker).
Symfony takes de response of all voters and makes a decision
// src/Security/PostVoter.php class PostVoter extends Voter { protected function supports($attribute, $subject) { // if the attribute isn't one we support, return false if (!in_array($attribute, array(self::VIEW, self::EDIT))) return false; // only vote on Post objects inside this voter if (!$subject instanceof Post) return false; return true; } protected function voteOnAttribute($attribute, $subject, TokenInterface $token) { $user = $token->getUser(); if (!$user instanceof User) { // the user must be logged in; if not, deny access return false; } // you know $subject is a Post object, thanks to supports /** @var Post $post */ $post = $subject; switch ($attribute) { case self::VIEW: return $this->canView($post, $user); case self::EDIT: return $this->canEdit($post, $user); } throw new \LogicException('This code should not be reached!'); } private function canEdit(Post $post, User $user) { // this assumes that the data object has a getOwner() method // to get the entity of the user who owns this data object return $user === $post->getOwner(); } }
Normally, only one voter will return true from support. but multiple can voter for the same action and object. => Access Decision strategy:
security: access_decision_manager: strategy: unanimous allow_if_all_abstain: false
To authorizate to access to a resource.
Security voters are the most granular way of checking permissions.
All voters are called each time you use the isGranted()
method on Symfony's authorization checker or call denyAccessUnlessGranted()
in a controller.
Ultimately, Symfony takes the responses from all voters and makes the final decision according to the strategy defined in the application, which can be: affirmative, consensus or unanimous.
Custom Voter implements VoterInterface
abstract class Voter implements VoterInterface { abstract protected function supports($attribute, $subject); abstract protected function voteOnAttribute($attribute, $subject, TokenInterface $token); }
In controller:
$this->denyAccessUnlessGranted('view', $post);
denyAccessUnlessGranted calls the voter system.
protected function voteOnAttribute($attribute, $subject, TokenInterface $token) { $user = $token->getUser(); if (!$user instanceof User) { // the user must be logged in; if not, deny access return false; } // you know $subject is a Post object, thanks to supports /** @var Post $post */ $post = $subject; switch ($attribute) { case self::VIEW: return $this->canView($post, $user); case self::EDIT: return $this->canEdit($post, $user); } throw new \LogicException('This code should not be reached!'); }
affirmative
(default); This grants access as soon as there is one voter granting access;
consensus
: This grants access if there are more voters granting access than denying;
unanimous
: This only grants access if there is no voter denying access. If all voters abstained from voting, the decision is based on the allow_if_all_abstain
config option (which defaults to false
).
HTTP/1.1 200 OK Date: Fri, 30 Oct 1998 13:19:41 GMT Server: Apache/1.3.3 (Unix) Cache-Control: max-age=3600, must-revalidate Expires: Fri, 30 Oct 1998 14:19:41 GMT Last-Modified: Mon, 29 Jun 1998 02:28:12 GMT ETag: "3e86-410-3596fbbc" Content-Length: 1040 Content-Type: text/html
Cache-Control HTTP Headers:
Validators and Validation: Most common validator is the time that the document last changed as communicated in Last-Modified header. HTTp .1. introduced Etag. Etag validation is becoming prevalent. Most modern Web servers will generate both.
Building a Cache -Aware site:
Gateway Cache (Symfony cache or varnish) Expiration, Validation, Expiration and Validation combined
Symfony reverse proxy
Create a Caching Kernel and call it from index.php
Make responses http cacheable. 4 response cache headers that you can set to enable caching: Cache-control, expires, etag, last-modified 2 models:
- Expiration caching: Cache invalidation is more difficult // cache for 3600 seconds $response->setSharedMaxAge(3600); - Validation caching: More complex but allows to invalidate as soon the content changes
FOSHttpCacheBundle
annotations (sensio/framwork bundle)
$item = $cache->getItem('markdown_'.md5($articleContent)); if (!$item->isHit()) { $item->set($markdown->transform($articleContent)); $cache->save($item); }
save it in redis can be checked in debug toolbar
to show all services ./bin/console debug:container --show-private
controllers: extend from abstractController or Controller
Name | Description |
---|---|
HttpFoundation | Defines an object-oriented layer for the HTTP specification. |
HttpKernel | Provides the building blocks to create flexible and fast HTTP-based frameworks. |
DependencyInjection | Allows you to standardize and centralize the way objects are constructed in your application. |
- | - |
ClassLoader | Loads your project classes automatically if they follow some standard PHP conventions. |
Console | Eases the creation of beautiful and testable command line interfaces. |
EventDispatcher | Implements the Mediator pattern in a simple and effective way to make projects truly extensible. |
Form | Provides tools to easy creating, processing and reusing HTML forms. |
Guard | Brings many layers of authentication together, making it much easier to create complex authentication systems where you have total control. |
Routing | Maps an HTTP request to a set of configuration variables. |
Security | Provides an infrastructure for sophisticated authorization systems. |
Serializer | Turns objects into a specific format (XML, JSON, Yaml, ...) and the other way around. |
Debug | Provides tools to ease debugging PHP code. |
Yaml | Loads and dumps YAML files. |
Templating | Provides all the tools needed to build any kind of template system. |
VarDumper | Provides mechanisms for walking through any arbitrary PHP variable. |
Asset | Manages URL generation and versioning of web assets |
Validator | Provides tools to validate classes. |
Translation | Provides tools to internationalize your application. |
Cache | Implements PSR-6 and PSR-16 caching mechanisms and provides adapters for popular caching backends (Redis, Memcache, APCu, etc.) |
- | - |
Messenger | Helps applications send and receive messages to/from other applications or via message queues. |
Process | Executes commands in sub-processes. $process = new Process(array('ls', '-lsa')); |
Lock | Creates and manages locks, a mechanism to provide exclusive access to a shared resource. |
OptionsResolver | Helps you configuring objects with option arrays. |
Intl | Replacemnt layer for the intl c extension(performs locale-aware operations- formmatiing, eoncoding, timezones, currencies). |
CssSelector | Converts CSS selectors to XPath expressions. |
Config | Helps you find, load, combine, autofill and validate configuration values. |
Workflow | Provides tools for managing a workflow or finite state machine. |
Ldap | Provides an LDAP client for PHP on top of PHP's ldap extension. |
Stopwatch | Provides a way to profile code instead of using microtime. |
PropertyInfo | Extracts info about the properties of PHP classes using metadata of: Doctrine, PHP Reflection, PHPdoc... |
PropertyAccess | Provides function to read and write from/to an object or array using a simple string notation. |
PHPUnit Bridge | Provides utilities to report legacy tests and usage of deprecated code and a helper for time-sensitive tests. |
Finder | Finds files and directories via an intuitive fluent interface. |
Filesystem | Provides basic utilities for the filesystem. |
BrowserKit | Simulates the behavior of a web browser. |
DomCrawler | Eases DOM navigation for HTML and XML documents. |
Dotenv | Parses .env files to make environment variables stored in them accessible via getenv(), $_ENV or $_SERVER. |
ExpressionLanguage | Provides an engine that can compile and evaluate expressions. |
- | - |
WebLink | Implements HTML5 Links, Preload and Resource Hints specifications to preload and prefetch documents through HTTP and HTTP/2 pushes. |
Contracts | A set of abstractions extracted out of the Symfony components. |
- | - |
Polyfill PHP 5.4 - 7.3 | Provides functions unavailable in releases prior to PHP 5.4 7.3. |
Polyfill APCu... | Provides apcu_* functions of the legacy APC extension. An multiple plyfills for diff extensions |
Polyfill Iconv | Provides a native PHP implementation of the php.net/iconv functions. |
Icu | Deprecated since October 2014, use the Intl component instead. |
Locale | Deprecated since 2.3, use the Intl component instead. |
------------ |
The OptionsResolver component is array_replace on steroids. It allows you to create an options system with required options, defaults, validation (type, value), normalization and more.
Is the symfony class that makes posible to get the Request object via an argument in a controller if it is type hinted. You can extend this funcionality.
Use symfony flex to create the app
Use symfony skeleton to create a new symfony app
Dont create bundles to organize your app
Use .env files to set env variables. The symfony app doesnt care where the db is or other env variables values.
Define application behavior related config in the config/services.yaml file
Use autowiring
Services should be private whenever possible (DI)
Use annotations to define the mapping infomation of the Doctrine Entities
Coding Standars (PSR-1, PSR-2, CS-fixer, sf4)
http://cs.sensiolabs.org/
Symfony follows the philosophy of "thin controllers and fat models".
Make your controller extend the AbstractController
base controller provided by Symfony and use annotations to configure routing, caching and security
Don't use the @Template
annotation to configure the template used by the controller.
return $this->render('default/index.html.twig', [
'posts' => $posts, ]);
Use the ParamConverter trick to automatically query for Doctrine entities when it's simple and convenient. Use lowercased snake_case for directory and template names. Use a prefixed underscore for partial templates in template names. Define your forms as PHP classes. Add buttons in the templates, not in the form classes or the controllers. {{ form_start(form) }}
{{ form_widget(form) }} <input type="submit" class="btn" value="Create" />
{{ form_end(form) }} Do not define your validation constraints in the form but on the object the form is mapped to. Use the XLIFF format for your translation files.
@Security
annotation;
Define a custom security voter to implement fine-grained restrictions.
use webpack encore
Define a functional test that at least checks if your application pages are successfully loading.
$this->assertTrue($client->getResponse()->isSuccessful());patch version x.x.X (every month) minor version x.X (may and november) major version X (every 2 years)
Version Type; Bugs are fixed for... ; Security issues are fixed for... Long-Term Support (LTS) ; 3 years ; 4 years
@deprecated
phpdoc to relevant classes, methods, properties, ...:PHP supports one error control operator: the at sign (@). When prepended to an expression in PHP, any error messages that might be generated by that expression will be ignored.
custom annotations Encore Argument resolver logical paths into physical paths