Symfony2: Using kernel events like preExecute to log requests

A couple of days ago, one of our developers mentioned wanting to log all the requests that hit a specific Symfony2 controller. Back in Symfony 1.2, you’d be able to easily accomplish this with a “preExecute” function in the specific controller that you want to log. We’d actually set something similar to this up and the code would end up looking like:

<?php

public function preExecute(){
  $this->jsonVariables = strlen($this->getRequest()->getContent()) 
                           ? json_decode( $this->getRequest()->getContent(), true ) : array();
      
   if( strlen(trim($this->getRequest()->getContent())) && $this->getUser()->getUserId() ){
      $uid = $this->getUser()->getUserId();
      $log = new JsonApiLog();
      $log->setUserId( $uid );
      $log->setRequestBody( $this->getRequest()->getContent() );
      $log->save();
   }
      
}

Symfony2 doesn’t have a “preExecute” hook in the same fashion as 1.2 but using the event system you can accomplish the same thing. What you’ll basically end up doing is configuring an event listener for the “kernel.controller” event, inject the EntityManager (or kernel) and then log the request.

The pertinent service configuration in YAML looks like:

    ctbe.apirequest_listner:
        class: CT\BEBundle\Event\ApiControllerRequest
        arguments: [@kernel]
        tags:
            - { name: kernel.event_listener, event: kernel.controller, method: onControllerRequest }

And then the corresponding class looks something like:

<?php

namespace CT\BEBundle\Event;

use CT\BEBundle\Entity\ApiRequest;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Kernel;

class ApiControllerRequest {
    
    private $kernel;
    
    public function __construct(Kernel $kernel){
        $this->kernel = $kernel;
    }
    
    public function onControllerRequest(FilterControllerEvent $event){
        
        $controller = $event->getRequest()->attributes->get('_controller');

        if( count(explode("::", $controller)) == 1 ){
            return;
        }
        
        list($className, $methodName) = explode("::", $controller);
        $className = explode("\\", $className);
        $className = $className[count($className) - 1];
        
        if( strpos($className, "Api") === false ){
            return;
        }

        $user = $this->kernel->getContainer()->get('security.context')->getToken()->getUser();
        
        $req = new ApiRequest();
        $req->setUser($user);
        $req->setRequest( $event->getRequest()->getContent() );
        $req->setUrl( $event->getRequest()->getRequestUri() );
        
        $this->kernel->getContainer()->get("doctrine")->getEntityManager()->persist($req);
        $this->kernel->getContainer()->get("doctrine")->getEntityManager()->flush($req);
    }
}

And thats about it.

Symfony2 forms without an entity and with a conditional validator

Recently on a project I had a situation where I was using the Symfony2 forms component without an entity. In addition to each field’s constraints, I needed to something similar to symfony 1.4’s conditional validator so that I could make sure that the form on the whole was valid. There are a bunch of docs out there on how to use callback functions on an entity to do this, however I didn’t see much on how to get the entire form that has no entity to do a callback. After reading some of the source code, found that you can set up some ‘form level’ constraints in the setDefaultOptions method. So it will look something like this:

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(
        array('constraints'=>array(new Callback(array('methods'=>array(array($this,'checkAvailableFunds'))))),			
	)
    );
	
}

You pass the Callback constraint an array methods which it can call. If you pass one of those methods is an array it is parsed as class::method. In my case by passing $this it uses the currently instantiated form, rather than trying to call the method statically.

From there you can do something like this:

public function checkAvailableFunds($data,ExecutionContextInterface $context)
{
    if($data['oneField']===1&&$data['twoField']===3){
        $context->addViolation('No or not enough giftcodes for selected export are available!');
    }
	 
}

The first parameter is the form’s data fields. From there you can add global level errors to the form, such as if a combination of fields are not valid.

Good luck out there.

PHP: How do you evaluate PHP skill?

We’re always on the hunt for talented LAMP developers and as a consequence we end up evaluating a decent amount of fairly diverse PHP code. We always ask potential employees for a code sample so that we can get a sense of their style and generally make sure they have their heads screwed on right. Because of this, we’ve been evaluating PHP samples from everything from Drupal modules, to batch processing scripts, and even “hardware hacks”.

During this process, one of the issues we’ve had is coming up with an objective rubric to evaluate the relative skill of a PHP developer. Although there are several broad criteria for evaluating code, I’ve been interested in coming up with PHP-centric benchmarks since they’re more directly applicable. Here’s a list of criteria that we’ve been working on to help us identify how familiar an engineer is with PHP.

Negative Signals

Unfortunately, it’s sometimes easier to spot negative signals so here are a few PHP specific “code smells” we’ve identified.

Re-inventing standard library functions

Often, inexperienced engineers won’t search for standard functions that’ll do exactly what they’re looking for. Not always a bad thing but it’s a sign of inexperience. An example would be:

<?php

function getDaysArray(){
  $dayOfTheWeek = array("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday");
  
  $arr = array();
  foreach($dayOfTheWeek as $day){
    $arr[$day] = $day;
  }

}

/** VS. **/

function getDaysArray(){
  $dayOfTheWeek = array("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday");
  return array_combine($dayOfTheWeek, $dayOfTheWeek);
}
Not really understanding the ORM (or SQL)

This one isn’t really PHP specific but we’ve noticed it a couple of times anyway. We’ll often see code that looks something like:

<?php

function getActiveUsersOfAge(){

  $em = $this->getContainer()->get('doctrine')->getEntityManager();
  $users = $em->getRepository("Acme:User")->createQueryBuilder("u")->getQuery()->getResult();

  $arr = array();
  foreach( $users as $us ){
    if( $us->getIsActive() && $us->getBirthdate() < "1991" ){
      $arr[] = $us;
    }
  }

  return $arr;
}

As you can see, the code returns all the results and then evaluates the criteria as opposed to passing the selection criteria to the ORM or SQL.

Everything is a global

Due to PHP’s global keyword it’s unfortunately really easy to throw encapsulation to the wind and just make everything a global. Because of that, we’ve seen code that looks like:

<?php

$someListOfData = "";
$someResult = null;

function sumTheList(){
  global $someListOfData, $someResult;
  // do stuff  
}

function writeTheResult {
  global $someListOfData, $someResult;
  // do some other stuff
}

Positive Signals

What we look for next is usually positive signals that an engineer is generally familiar with PHP. These are generally things you’d pick up after you’ve written a fair amount of PHP.

Has implemented __toString() somewhere

I know this one will be controversial, but my sense is that if an engineer implements __toString() somewhere in PHP they probably have a decent familiarity with the language since its something you have to “seek out”. A canonical example would be something like:

<?php

class User {
  private $firstName;
  private $lastName;


  public function __toString(){
    return $this->firstName . " " . $this->lastName;
  }  
}
Uses output buffering

Output buffering is a bit exotic but it’s indispensable when building web apps without a framework. It also certainly demonstrates a level of familiarity with PHP. A good example would be capturing output from a template and returning it inside some JSON:

<?php

ob_start(function($output){
  return json_encode( array("html" => $output) );
});

require "someTemplateFile.html";

ob_end_flush();

Voodoo Positive Signals

Finally, the last couple of things we’ve been looking for are “exotic” techniques that really demonstrate that someone “gets” PHP. Granted, some (most?) of these are bad ideas in production they do convey a certain level of understanding.

Classes that implement the ArrayAccess interface

Since PHP relies so heavily on arrays, building classes that implement the ArrayAccess interface makes them work more naturally in PHP and definitely demonstrates a strong level of familiarity with the language. An example would be:

<?php

class FormInput implements arrayaccess {

    private $values = array();

    public function __construct($values) {
        $this->values = $values;
    }

    public function offsetSet($offset, $value) {

    }

    public function offsetExists($offset) {
        return isset($this->values[$offset]);
    }

    public function offsetUnset($offset) {
        unset($this->values[$offset]);
    }

    public function offsetGet($offset) {
        return isset($this->values[$offset]) ? htmlentities($this->values[$offset]) : null;
    }
   
}

$form = new FormInput($_REQUEST);
echo $form["username"] . "\n";
Using __set, __get, or __call

Although, using any of these in production is questionable at best, they undeniably do convey a sense that whoever used them knows their way around PHP. An example (from the documentation) would be:

<?php

// From http://www.php.net/manual/en/language.oop5.overloading.php#object.call

class MethodTest
{
    public function __call($name, $arguments)
    {
        // Note: value of $name is case sensitive.
        echo "Calling object method '$name' "
             . implode(', ', $arguments). "\n";
    }

    /**  As of PHP 5.3.0  */
    public static function __callStatic($name, $arguments)
    {
        // Note: value of $name is case sensitive.
        echo "Calling static method '$name' "
             . implode(', ', $arguments). "\n";
    }
}

$obj = new MethodTest;
$obj->runTest('in object context');

Anyway, like everything I’d take this list with a big grain of salt. We’d also love any input or feedback.

HTTPs, Reverse Proxys, and Port 80!?

Recently we were getting ready to deploy a new project which functions only over SSL. The project is deployed on AWS using the Elastic Load Balancers (ELB). We have the ELB doing the SSL termination to reduce the load on the server and to help simply management of the SSL certs. Anyways the the point of this short post. One of the developers noticed that on some of the internal links she kept getting a link something like “https://dev.app.com:80/….”, it was properly generating the link to HTTPS but then specify port 80. Of course your browser really does not like that as its conflicting calls of port 80 and 443. After a quick look into the project we found that we had yet to enable the proxy headers and specify the proxy(s), it was we had to turn on trust_proxy_headers. However, doing this did not fix the issue. You must in addition to enable the headers specify which ones you trust. This can be easily done via the following:

<?php

use Symfony\Component\ClassLoader\ApcClassLoader;
use Symfony\Component\HttpFoundation\Request;

$loader = require_once __DIR__.'/../app/bootstrap.php.cache';

// Use APC for autoloading to improve performance.
// Change 'sf2' to a unique prefix in order to prevent cache key conflicts
// with other applications also using APC.
/*
$loader = new ApcClassLoader('sf2', $loader);
$loader->register(true);
*/

require_once __DIR__.'/../app/AppKernel.php';
//require_once __DIR__.'/../app/AppCache.php';

$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
//$kernel = new AppCache($kernel);
$request = Request::createFromGlobals();

Request::setTrustedProxies(array('192.168.1.1'));

// If it is only available via the proxy:
//Request::setTrustedProxies(array($request->server->get('REMOTE_ADDR')));

$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

Here is a very simple example of how you could specify them. You just let it know the IP’s of the proxy(s) and it will then properly generate your links.

You can read up on this more in the Symfony documentation on trusting proxies.

Anyways just wanted to put throw this out there incase you see this and realize you forgot to configure the proxy in your app!

PHP: Seven PHP developers you should be following on Twitter

A couple of days ago, a friend of mine looking to engage in the community asked me which experienced PHP developers he should follow on Twitter. An interesting question, and as I started looking through the @setfive follower list I realized we really don’t follow very many. Anyway, not wanting to leave him hanging, I put together a list of 5 developers that I thought were a good start.

This is obviously just a start, but I’d love everyone’s help to build out a list of solid PHP developers to follower on Twitter. If you leave them in the comments, we’ll pull together a single list and update this post once we have it!

Fabien Potencier

Co-founder and CEO for @SensioLabs, founder and project lead for @Symfony. Tweeting from @fabpot and on the web at http://fabien.potencier.org

Jonathan Wage

Husband to @meganswage and director of technology @OpenSky. @ServerGrove @Symfony @DoctrineORM Tweeting from @jwage and on the web at http://jwage.com/

Kris Wallsmith

Lead architect & symfony guru at @opensky. lead dev on assetic, buzz, spork. father of 3, widower of 1. Tweeting from @kriswallsmith and on the web at http://kriswallsmith.net/

Chris Corbyn

Nerd, englishman, chatterbox, cake-a-holic, celery hater, Italophile, nu-melbournite, SitePoint/Flippa code monkey. I’m also @cosadici. Tweeting from @d11wtq and on the web at http://chriscorbyn.co.uk

Dustin Whittle

Technologist, Architect, Open Source Advocate Tweeting from @dustinwhittle and on the web at http://dustinwhittle.com/

Dries Buytaert

Creator of Drupal, Drupal project lead, co-founder and CTO of Acquia, and Mollom spam fighter. Tweeting from @Dries and on the web at http://buytaert.net/

Lukas Smith

My twitter alter-ego is all about PHP and databases. My coding addiction is financed by @liip. Tweeting from @liip and on the web at http://www.liip.ch/en

Joseph Bielawski

Software Developer - #Symfony2 #PHP Polish Twitter Translator Tweeting from @stloyd and on the web at https://github.com/stloyd

Robin Muilwijk

Board member, eZ Publish Community Project Board : Open Source Advocate : Community Management : Social Media : Civil Servant : Information / Data Engineer Tweeting from @i_robin and on the web at http://www.linkedin.com/in/robinmuilwijk

Anthony Ferrara

Anything Regarding Software Security, Performance, Quality and Architecture… Tweeting from @ircmaxell and on the web at http://blog.ircmaxell.com/

Nikita Popov

18 year old student enjoying programming :) Tweeting from @nikita_ppv and on the web at http://nikic.github.io/

Igor Wiedler

Philosopher. Tweeting from @igorwesome and on the web at https://igor.io/

Matthew Weier O’Phinney

PHP and ZF Developer; crazed father of two. Tweeting from @mwop and on the web at http://www.mwop.net/

William Durand

Student by day, full stack developer by night. Open-Source evangelist all the time. Tweeting from @couac and on the web at http://careers.stackoverflow.com/williamdurand

Jordi Boggiano

Passionate web developer, specialized in web performance and php, #Composer lead, #Symfony2 developer. Partner at @nelmio, information junkie and speaker. Tweeting from @seldaek and on the web at http://seld.be/

Giorgio Sironi

Developer at @Onebip. I search for the harmony between form and context. Software, science, economics. Tweeting from @giorgiosironi and on the web at http://www.giorgiosironi.com/