Symfony2 aided by PhpStorm

An earlier post here did not help me much whenever I went back and tried to debug a bit more on a project at hand.

The problems I had:

  • I had set it up my phpstorm with xdebug to debug projects on symfony2, however I had not tried in a while so I forgot how I did it in the first place.
  • My debugger had problems recognizing the .cache extension that the bootstrap cache files had in Symfony2. Because of this I could not enter with breakpoints into any of the files of my project.
  • I did not understand the process of debugging an app in Symfony2 well so I was scared about something may break or that it would be too complex.
  • I thought it was faster to use var_dump or print_r commands to output variables to the console to check my results.

So firstly, in order to get back at speed with phpstorm debugging I just fired up the listening connections icon, it is a phone icon and then I created or recreated some debug configurations. Set a server to my project local domain and all set. Then I was at least getting the connection right and Ctrl+F9 and Ctrl+F8 were my friends.

Secondly, I was told by phpstorm support person that I had to associate the cache file extensions as text files. This I did by going to Settings > File Type under IDE settings and then assign or reassign the extension *.cache which if already assigned asked me to forcefully reassign to new association. This solved the problem of stepping into beyon the bootstrap cache files.

Here I show a valid configuration for my xdebug.ini that sets the configuration for phpstorm xdebug communication.

$cat /etc/php5/cli/conf.d/xdebug.ini zend_extension=/usr/lib/php5/20090626+lfs/xdebug.so
xdebug.remote_host=127.0.0.1
xdebug.remote_enable=1
xdebug.remote_port=9000
xdebug.remote_handler=dbgp
xdebug.remote_mode=req

A more involved version of the configuration file can be found here as shared by phpstorm support person:

root@mvubdevel:~# cat /etc/php5/fpm/conf.d/xdebug.ini
zend_extension=/usr/lib/php5/20090626+lfs/xdebug.so
xdebug.remote_enable=1
xdebug.remote_handler=dbgp
xdebug.remote_mode=req
xdebug.idekey=default
xdebug.remote_host=127.0.0.1
xdebug.remote_autostart=0
xdebug.var_display_max_children=512
xdebug.var_display_max_data=32000
xdebug.var_display_max_depth=8
xdebug.remote_log=/tmp/xdbg.log
;xdebug.profiler_enable=1
;xdebug.profiler_output_dir=/tmp
;xdebug.profiler_output_name=cachegrind.out.%u

Following I am trying to do a snap shot of each of the steps that an Symfony2 app undergoes:

Symfony2 starts on the front controller and directly loads the UniversalClassLoader. This is in our development version which does not use the bootstrap cache file. Here it loads it so that the autoload file in our app/ folder is called every time. It also loades the Kernel.

require_once __DIR__.'/../vendor/symfony/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
require_once __DIR__.'/../app/autoload.php';
require_once __DIR__.'/../app/AppKernel.php';

When creating a kernel class with $kernel = new AppKernel('dev', true); Symfony2 loads basic startup variables and timing with the constructor of the kernel class. Then handles responsibility over to the handle methods of the class $kernel->handle(Request::createFromGlobals())->send();.

Symfony2 right away starts loading the classes needed to execute the code of the application by pulling them with the loadClass method of DebugUniversalClassLoader.php.

Meanwhile there is a Request object which is initialized by the createFromGlobals() method. This basically is the building block for HTTP request protocol.

After the above steps take place the handling of the request is fired up after the Kernel has been set as booted.

A Response object has been created by all the grinding associated with the steps above. Now it is time to call the method send from the Response object.

The job has been accomplished, and now it is time to remove all used material.

Symfony2 proceeds to the destruction of serializable session class. For his it has a destruct magic method and before it does destructs the class it saves the session if the session started parameter is set. Else it will just destruct and not persist anything.

public function __destruct()
    {
        if (true === $this->started) {
            $this->save();
        }
    }

The Monolog package also creates an object that implements an Abstract Handler that follows destruction of the session object above. The Monolog object then calls its destruct method and basically calls a method which is empty.
public function __destruct()

    {
        $this->close();
    }

For some reason the monolog handler is called two or three times.

After I inserted several break points into my controller, right before it gets redirected then I stepped through the code and found that some variables were not properly defined.

Some more information can be found in the cookbook entry that I was pointed to here and also here.

Symfony2 Debugging with Twig Extensions

There use to be a time when one could add twig extensions in this way:

extensions: [twig.extensions.debug]

However the newer way of adding extensions is as services using tags to let your setup know that these services are indeed twig.extensions and they need to be called when twig related services are executed. So we just need to drop this into our config.yml to make this work and also of course have the debug parameter on your config.yml set to true:

twig:
    debug:            %kernel.debug%

And here is the code for the extensions:

services:
   twig.extension.text:
       class: Twig_Extensions_Extension_Text
       tags:
           - { name: twig.extension }
   twig.extension.debug:
       class: Twig_Extensions_Extension_Debug
       tags:
           - { name: twig.extension }

And its usage according to documentation:

{% debug variabletodump %}

Calling Commands Within Commands in Symfony2

I was trying to create a command. The intent goes beyond the exercise as there are some guys already who are trying to setup an entire system from the console. But for now we will just do the command creation. The idea is to call commands within commands and for that we have the following information here which is too simple for our purposes and also here where we are told how to call commands within commands but the documentation does not elaborate too much on its uses and interesting applications.

So here I paste the code for a basic command:

<?php
namespace Cordova\CrownBundle\Command;
 
use Symfony\Bundle\FrameworkBundle\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\ArrayInput;
 
class SetupCommand extends Command
{
    protected function configure()
    {
        $this
            ->setName('crown:setup')
            ->setDescription('Crown Setup')
            ->addArgument('yesno', InputArgument::REQUIRED, 'Do you want to fire up setup? (y/n)')
            //->addOption('yell', null, InputOption::VALUE_NONE, 'If set, the task will yell in uppercase letters')
        ;
    }
 
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $yesno = $input->getArgument('yesno');
        if ($yesno == 'y') {
            $text = 'Running setup ... ';
            $command = $this->getApplication()->find('doctrine:fixtures:load');
            $arguments = array(
                //'--force' => true
                ''
            );
            $input = new ArrayInput($arguments);
            $returnCode = $command->run($input, $output);
            if($returnCode == 0) {
                $text .= 'fixtures successfully loaded ...';
            }
 
        } else {
            $text = 'Exiting ...';
        }
 
        /*if ($input->getOption('yell')) {
            $text = strtoupper($text);
        }*/
 
        $output->writeln($text);
    }
}

For now we just are loading the fixtures of our system. Of course this command at the moment is found redundant. However it can be well expanded into something more complex. Thanks for reading. Please consider donating.

Set Date Ranges on Symfony2 Forms

Recently I was trying to render birth dates forms for a given entity and usually ages go way back than what is hard coded in sf2 which is +5/-5 years. So I went in and dug a little bit on the documentation, but it was not very clear how to set the ranges to custom.
So I went straight into the code here and found how to turn my:

->add('dob', 'date')

Into:

->add('dob', 'date', array(
    'years' => range(date('Y') - 100, date('Y') - 20)
   )
)

For more ranges we can check the link above:

public function getDefaultOptions(array $options)
{
    return array(
        'years'         => range(date('Y') - 5, date('Y') + 5),
        'months'        => range(1, 12),
        'days'          => range(1, 31),
        'widget'        => 'choice',
        'input'         => 'datetime',
        'format'        => \IntlDateFormatter::MEDIUM,
        'data_timezone' => null,
        'user_timezone' => null,
        'empty_value'   => null,
        // Don't modify \DateTime classes by reference, we treat
        // them like immutable value objects
        'by_reference'  => false,
    );
}