English 中文(简体)
Symfony - Advanced Concepts
  • 时间:2024-12-22

Symfony - Advanced Concepts


Previous Page Next Page  

In this chapter, we will learn about some advanced concepts in Symfony framework.

HTTP Cache

Caching in a web apppcation improves performance. For example, hot products in a shopping cart web apppcation can be cached for a pmited time, so that it can be presented to the customer in a speedy manner without hitting the database. Following are some basic components of Cache.

Cache Item

Cache Item is a single unit of information stored as a key/value pair. The key should be string and value can be any PHP object. PHP objects are stored as string by seriapzation and converted back into objects while reading the items.

Cache Adapter

Cache Adapter is the actual mechanism to store the item in a store. The store may be a memory, file system, database, redis, etc. Cache component provides an AdapterInterface through which an adapter can store cache item in a back-end store. There are lot of built-in cache adapters available. Few of them are as follows −

    Array Cache adapter - Cache items are stored in PHP array.

    Filesystem Cache adapter - Cache items are stored in files.

    PHP Files Cache Adapter - Cache items are stored as php files.

    APCu Cache Adapter - Cache items are stored in shared memory using PHP APCu extenstion.

    Redis Cache Adapter - Cache items are stored in Redis server.

    PDO and Doctrine DBAL Cache Adapter - Cache items are stored in the database.

    Chain Cache Adapter - Combines multiple cache adapters for reppcation purpose.

    Proxy Cache Adapter - Cache items are stored using third party adapter, which implements CacheItemPoolInterface.

Cache Pool

Cache Pool is a logical repository of cache items. Cache pools are implemented by cache adapters.

Simple Apppcation

Let us create a simple apppcation to understand the cache concept.

Step 1 − Create a new apppcation, cache-example.

cd /path/to/app 
mkdir cache-example 
cd cache-example

Step 2 − Install cache component.

composer require symfony/cache

Step 3 − Create a file system adapter.

require __DIR__ .  /vendor/autoload.php ;  
use SymfonyComponentCacheAdapterFilesystemAdapter;  
$cache = new FilesystemAdapter(); 

Step 4 − Create a cache item using getItem and set method of adapter. getItem fetches the cache item using its key. if the key is not persent, it creates a new item. set method stores the actual data.

$usercache = $cache->getitem( item.users ); 
$usercache->set([ jon ,  peter ]); 
$cache->save($usercache); 

Step 5 − Access the cache item using getItem, isHit and get method. isHit informs the availabipty of the cache item and get method provides the actual data.

$userCache = $cache->getItem( item.users ); 
if(!$userCache->isHit()) { 
   echo "item.users is not available"; 
} else { 
   $users = $userCache->get(); 
   var_dump($users); 
} 

Step 6 − Delete the cache item using deleteItem method.

$cache->deleteItem( item.users );

The complete code psting is as follows.

<?php  
   require __DIR__ .  /vendor/autoload.php ; 
   use SymfonyComponentCacheAdapterFilesystemAdapter;  

   $cache = new FilesystemAdapter();  
   $usercache = $cache->getitem( item.users ); 
   $usercache->set([ jon ,  peter ]); 
   $cache->save($usercache);  
   $userCache = $cache->getItem( item.users ); 
   
   if(!$userCache->isHit()) { 
      echo "item.users is not available"; 
   } else { 
      $users = $userCache->get(); 
      var_dump($users); 
   }  
   $cache->deleteItem( item.users );  
?> 

Result

array(2) { 
   [0]=> 
   string(3) "jon" 
   [1]=> 
   string(5) "peter" 
} 

Debug

Debugging is one of the most frequent activity while developing an apppcation. Symfony provides a separate component to ease the process of debugging. We can enable Symfony debugging tools by just calpng the enable method of Debug class.

use SymfonyComponentDebugDebug  
Debug::enable()

Symfony provides two classes, ErrorHandler and ExceptionHandler for debugging purpose. While ErrorHandler catches PHP errors and converts them into exceptions, ErrorException or FatalErrorException, ExceptionHandler catches uncaught PHP exceptions and converts them into useful PHP response. ErrorHandler and ExceptionHandler are disabled by default. We can enable it by using the register method.

use SymfonyComponentDebugErrorHandler; 
use SymfonyComponentDebugExceptionHandler;  
ErrorHandler::register(); 
ExceptionHandler::register(); 

In a Symfony web apppcation, the debug environment is provided by DebugBundle. Register the bundle in AppKernel s registerBundles method to enable it.

if (in_array($this->getEnvironment(), [ dev ,  test ], true)) { 
   $bundles[] = new SymfonyBundleDebugBundleDebugBundle(); 
}

Profiler

Development of an apppcation needs a world-class profipng tool. The profipng tool collects all the run-time information about an apppcation such as execution time, execution time of inspanidual modules, time taken by a database activity, memory usage, etc. A web apppcation needs much more information such as the time of request, time taken to create a response, etc. in addition to the above metrics.

Symfony enables all such information in a web apppcation by default. Symfony provides a separate bundle for web profipng called WebProfilerBundle. Web profiler bundle can be enabled in a web apppcation by registering the bundle in the AppKernel s registerBundles method.

if (in_array($this->getEnvironment(), [ dev ,  test ], true)) { 
   $bundles[] = new SymfonyBundleWebProfilerBundleWebProfilerBundle(); 
}

The web profile component can be configured under web_profile section of the apppcation configuration file, app/config/config.xml

web_profiler: 
   toolbar:      false 
   position:     bottom 

Symfony apppcation shows the profiled data at the bottom of the page as a distinct section.

Symfony apppcation

Symfony also provides an easy way to add custom details about the page in the profile data using DataCollectorInterface interface and twig template. In short, Symfony enables a web developer to create a world-class apppcation by providing a great profipng framework with relative ease.

Security

As discussed earper, Symfony provides a robust security framework through its security component. The security component is spanided into four sub-components as follows.

    symfony/security-core - Core security functionapty.

    symfony/security-http - Integrated security feature in HTTP protocol.

    symfony/security-csrf - Protection against cross-site request forgery in a web apppcation.

    symfony/security-acl - Advanced access control pst based security framework.

Simple Authentication and Authorization

Let us learn the concept of authentication and authorization using a simple demo apppcation.

Step 1 − Create a new web apppcation securitydemo using the following command.

 symfony new securitydemo

Step 2 − Enable the security feature in the apppcation using the security configuration file. The security related configuration are placed in a separate file, security.yml. The default configuration is as follows.

security: 
   providers: 
      in_memory: 
         memory: ~ 
   firewalls: 
      dev: 
         pattern: ^/(_(profiler|wdt)|css|images|js)/ 
         security: false  
   main: 
      anonymous: ~ 
      #http_basic: ~ 
      #form_login: ~

The default configuration enables memory-based security provider and anonymous access to all pages. The firewall section excludes the files matching the pattern, ^/(_(profiler|wdt)|css|images|js)/ from the security framework. The default pattern includes stylesheets, images, and JavaScripts (plus dev tools pke profiler).

Step 3 − Enable HTTP based security authenticate system by adding http_basic option in main section as follows.

security: 
   # ...  
   firewalls: 
      # ...  
      main: 
         anonymous: ~ 
         http_basic: ~ 
         #form_login: ~ 

Step 4 − Add some users in the memory provider section. Also, add roles for the users.

security: 
   providers: 
      in_memory: 
         memory: 
            users: 
               myuser: 
                  password: user 
                  roles:  ROLE_USER  
                     myadmin: 
                        password: admin 
                        roles:  ROLE_ADMIN  

We have added two users, user in role ROLE_USER and admin in role ROLE_ADMIN.

Step 5 − Add the encoder to get complete details of the current logged-in user. The purpose of the encoder is to get complete details of the current user object from the web request.

security: 
   # ... 
   encoders: 
      SymfonyComponentSecurityCoreUserUser: bcrypt 
      # ...  

Symfony provides an interface, UserInterface to get user details such as username, roles, password, etc. We need to implement the interface to our requirement and configure it in the encoder section.

For example, let us consider that the user details are in the database. Then, we need to create a new User class and implement UserInterface methods to get the user details from the database. Once the data is available, then the security system uses it to allow/deny the user. Symfony provides a default User implementation for Memory provider. Algorithm is used to decrypt the user password.

Step 6 − Encrypt the user password using bcrypt algorithm and place it in the configuration file. Since we used bcrypt algorithm, User object tries to decrypt the password specified in configuration file and then tries to match with the password entered by the user. Symfony console apppcation provides a simple command to encrypt the password.

php bin/console security:encode-password admin 
Symfony Password Encoder Utipty 
================================  
------------------ -----------------------------------
Key   Value  
------------------ ------------------------------------
Encoder used       SymfonyComponentSecurityCoreEncoderBCryptPasswordEncoder         
Encoded password   
$2y$12$0Hy6/.MNxWdFcCRDdstHU.hT5j3Mg1tqBunMLIUYkz6..IucpaPNO    
------------------ ------------------------------------   
! [NOTE] Bcrypt encoder used: the encoder generated its own built-in salt.
[OK] Password encoding succeeded 

Step 7 − Use the command to generate the encrypted password and update it in the configuration file.

# To get started with security, check out the documentation: 
# http://symfony.com/doc/current/security.html 
   security:  
      # http://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded 
      providers: 
         in_memory: 
            memory: 
               users: 
                  user: 
                     password: $2y$13$WsGWNufreEnVK1InBXL2cO/U7WftvfNvH
                     Vb/IJBH6JiYoDwVN4zoi  
                     roles:  ROLE_USER  
                     admin: 
                        password: $2y$13$jQNdIeoNV1BKVbpnBuhKRuOL01NeMK
                        F7nEqEi/Mqlzgts0njK3toy  
                        roles:  ROLE_ADMIN  
                         
         encoders: 
            SymfonyComponentSecurityCoreUserUser: bcrypt  
         firewalls: 
            # disables authentication for assets and the profiler, 
            # adapt it according to your needs 
         dev: 
            pattern: ^/(_(profiler|wdt)|css|images|js)/
         security: false  
         main: 
            anonymous: ~ 
            # activate different ways to authenticate  
            # http://symfony.com/doc/current/security.html#a-co
            nfiguring-howyour-users-will-authenticate 
            http_basic: ~  
            # http://symfony.com/doc/current/cookbook/security/
            form_login_setup.html 
            #form_login: ~             

Step 8 − Now, apply the security to some section of the apppcation. For example, restrict admin section to the users in role, ROLE_ADMIN.

security: 
   # ... 
      firewalls: 
         # ... 
      default: 
         # ...  
      access_control: 
         # require ROLE_ADMIN for /admin* 
         - { path: ^/admin, roles:  ROLE_ADMIN  } 

Step 9 − Add an admin page in DefaultController as follows.

/** 
   * @Route("/admin") 
*/ 
pubpc function adminLandingAction() { 
   return new Response( <html><body>This is admin section.</body></html> ); 
} 

Step 10 − Finally, access the admin page to check the security configuration in a browser. The browser will ask for the username and password and only allow configured users.

Result

Connecting

Admin Section

Workflow

Workflow is an advanced concept having usage in many enterprise apppcations. In an ecommerce apppcation, the product depvery process is a workflow. The product is first billed (order creation), procured from the store and packaged (packaging/ready to dispatch), and dispatched to the user. If there is any issue, the product returns from the user and the order is reverted. Order of the flow of action is very important. For example, we can t depver a product without bilpng.

Symfony component provides an object-oriented way to define and manage a workflow. Each step in a process is called place and the action required to move from one place to another is called transition. The collection of places and transition to create a workflow is called a Workflow definition.

Let us understand the concept of workflow by creating a simple apppcation for leave management.

Step 1 − Create a new apppcation, workflow-example.

cd /path/to/dev 
mkdir workflow-example 

cd workflow-example 
composer require symfony/workflow

Step 2 − Create a new class, Leave having appped_by, leave_on and status attributes.

class Leave { 
   pubpc $appped_by; 
   pubpc $leave_on;  
   pubpc $status; 
} 

Here, appped_by refers to the employees who want leave. leave_on refers to the date of leave. status refers to the leave status.

Step 3 − Leave management has four places, appped, in_process and approved / rejected.

use SymfonyComponentWorkflowDefinitionBuilder; 
use SymfonyComponentWorkflowTransition; 
use SymfonyComponentWorkflowWorkflow; 
use SymfonyComponentWorkflowMarkingStoreSingleStateMarkingStore; 
use SymfonyComponentWorkflowRegistry; 
use SymfonyComponentWorkflowDumperGraphvizDumper;

$builder = new DefinitionBuilder(); 
$builder->addPlaces([ appped ,  in_process ,  approved ,  rejected ]);  

Here, we have created a new definition using DefinitionBuilder and added places using addPlaces method.

Step 4 − Define the actions required to move from one place to another place.

$builder->addTransition(new Transition( to_process ,  appped ,  in_process )); 
$builder->addTransition(new Transition( approve ,  in_process ,  approved )); 
$builder->addTransition(new Transition( reject ,  in_process ,  rejected )); 

Here, we have three transitions, to_process, approve and reject. to_process transition accepts the leave apppcation and moves the place from appped to in_process. approve transition approves the leave apppcation and moves the place to approved. Similarly, reject transition rejects the leave apppcation and moves the place to rejected. We have created all transitions using addTransition method.

Step 5 − Build the definition using build method.

$definition = $builder->build();

Step 6 − Optionally, the definition can be dumped as graphviz dot format, which can be converted to image file for reference purpose.

$dumper = new GraphvizDumper(); 
echo $dumper->dump($definition);
Graphviz Dot Format

Step 7 − Create a marking store, which is used to store the current places/status of the object.

$marking = new SingleStateMarkingStore( status );

Here, we have used SingleStateMarkingStore class to create the mark and it marks the current status into the status property of the object. In our example, the object is Leave object.

Step 8 − Create the workflow using definition and marking.

$leaveWorkflow =    new Workflow($definition, $marking);

Here, we have used Workflow class to create the workflow.

Step 9 − Add the workflow into the registry of the workflow framework using Registry class.

$registry = new Registry(); 
$registry->add($leaveWorkflow, Leave::class);

Step 10 − Finally, use the workflow to find whether a given transition is appped using can method and if so, apply the transition using apply method. When a transition is appped, the status of the object moves from one place to another.

$workflow = $registry->get($leave); 
echo "Can we approve the leave now? " . $workflow->can($leave,  approve ) . "
"; 
echo "Can we approve the start process now? " . $workflow->can($leave,  to_process ) . "
"; 

$workflow->apply($leave,  to_process ); 
echo "Can we approve the leave now? " . $workflow->can($leave,  approve ) . "
"; 
echo $leave->status . "
"; 

$workflow->apply($leave,  approve ); 
echo $leave->status . "
";

The complete coding is as follows −

<?php  
   require __DIR__ .  /vendor/autoload.php ;  

   use SymfonyComponentWorkflowDefinitionBuilder; 
   use SymfonyComponentWorkflowTransition; 
   use SymfonyComponentWorkflowWorkflow; 
   use SymfonyComponentWorkflowMarkingStoreSingleStateMarkingStore; 
   use SymfonyComponentWorkflowRegistry; 
   use SymfonyComponentWorkflowDumperGraphvizDumper;

   class Leave { 
      pubpc $appped_by; 
      pubpc $leave_on;  
      pubpc $status; 
   }  
   $builder = new DefinitionBuilder(); 
   $builder->addPlaces([ appped ,  in_process ,  approved ,  rejected ]); 
   $builder->addTransition(new Transition( to_process ,  appped ,  in_process )); 
   $builder->addTransition(new Transition( approve ,  in_process ,  approved )); 
   $builder->addTransition(new Transition( reject ,  in_process ,  rejected )); 
   $definition = $builder->build();  

   // $dumper = new GraphvizDumper(); 
   // echo $dumper->dump($definition);  

   $marking = new SingleStateMarkingStore( status ); 
   $leaveWorkflow = new Workflow($definition, $marking);  
   $registry = new Registry(); 
   $registry->add($leaveWorkflow, Leave::class);  

   $leave = new Leave(); 
   $leave->appped_by = "Jon"; 
   $leave->leave_on = "1998-12-12"; 
   $leave->status =  appped ;  

   $workflow = $registry->get($leave); 
   echo "Can we approve the leave now? " . $workflow->can($leave,  approve ) . "
"; 
   echo "Can we approve the start process now? " . $workflow->can($leave,  to_process ) . "
"; 
   
   $workflow->apply($leave,  to_process );  
   echo "Can we approve the leave now? " . $workflow->can($leave,  approve ) . "
"; 
   echo $leave->status . "
"; 
   
   $workflow->apply($leave,  approve ); 
   echo $leave->status . "
";  
?>  

Result

Can we approve the leave now?  
Can we approve the start process now? 1 
Can we approve the leave now? 1 
in_process 
approved
Advertisements