Symfony2: usort() Array was modified by the user comparison function

Earlier this week we were repeatedly getting notifications about a “usort() Array was modified by the user comparison function” warning for one of our new Symfony2 projects. The weird thing was the sort function was relatively straightward and looked something like:

Obviously not modifying the array. Anyway, Daum dug up this StackOvervlow thread which suggested that using introspection methods silently modify the array and trigger the warning but I’m clearly not using any of those either.

After some more poking around, we ran across a Doctrine bug specifically mentioning the usort issue. It turns out, because of how Doctrine’s lazy loading functionality works if the usort callback function causes Doctrine to lazy load it’ll silently modify the array and cause that warning. Great, so how do you fix it? It’s actually pretty straightforward, you just need to force the lazy loading before sorting the collection. I ended up with something like:

Anyway, fun fact of the day. Questions and comments always welcome.

Symfony2: Creating optgroups with an Entity type

Last week, I was working on a Symfony2 app where I wanted to generate optgroup tags inside the select box of an Entity form type. After poking around, I ran across a StackOverflow answer explaining how to do it. Basically, it turns out what you have to do is manually return a “choices” array from a class that has access to the Entity Manager. I ended up adding a method to my custom repository and passing that repository into my form:

It’s a bit messy and I’m surprised there isn’t an option on the Entity Type to pass in a callback with access to the Entity Manager to generate a choice list. Looking at the source of DoctrineType it looks like you could potentially create a custom type to extend the Entity type and then access the em from your custom function. Even that though, seems like overkill to accomplish something that is reasonably common.

Symfony2 and Impersonating Users, a Heads Up

Recently I was working on a project in which it admins were able to impersonate other users.  It’s a fairly easy task to add to Symfony2, merely adding a switch_user reference to your firewall can make it possible, consult the Symfony docs for more on that.  One thing I noticed was that every now and then when testing I would get weird errors after switching between multiple users, however it didn’t always happen.  After some digging around, it turns out when you switch user it does not clear that sessions attributes, ie if you set attribute ‘hello’ to value ‘world’ it would persist after you’ve impersonated another user.  This caused a few issues as on this application we used the session to store a few things like which set of database connections you currently use.

After looking at the SecurityBundle configuration setup it was clear that there wasn’t any options to have it clear all session attributes on switch user.  At this point it was clear I needed to use an event listener as the firewall dispatched the SwitchUserEvent when a user successfully switched user.  Below is an excerpt from my services.yml
This makes it so that it will call the following code on a successful impersonation of a user:

It’s as simple as that, you can get the actual user by calling $event->getTargetUser(). Long story short, the session can have some tainted values when using switch user as all attributes are not cleared.

Symfony2 Cache Clear in Prod Fails? Using JMSDiExtraBundle?

Just a quick one out there as I saw a bunch of posts trying to get around the following error:

The error of the occurs on the cache warmup part of the clearing. After looking around people referenced a bunch of different solutions, the most popular being to do a –no-warmup. However, we wanted to try to fix the problem rather than just avoid warming the cache.

The solution fairly quick and really easy. This is using 2.1.X of Symfony and 1.3.* of the JMSDiExtraBundle. Before we had in our AppKernel.php the following:

I noticed in one of the docs for the JMSDiExtraBundle it has the JMSAopBundle in the list BEFORE JMSDiExtraBundle. We tried moving this up and had success, so the final one looked like:

Hope this saves someone some time.

Symfony2: Configuring VichUploaderBundle and Gaufrette to use AmazonS3

Last week, I was looking to install the VichUploaderBundle into a Symfony2 project to automatically handle file uploads. As I was looking through the Vich documentation I ran across a chunk describing being able to use Gaufrette to skip the local filesystem and push files directly to Amazon S3. Since we’d eventually need to load balance the app and push uploaded files to S3 anyway, I decided to set it up out of the gate. Unfortunately, the documentation for setting up Vich with Gaufrette is a bit opaque so here’s a step by step guide to getting it going.

Install Everything

The first thing you’ll want to do is install all the required packages. If you’re using Composer, the following will work:

Once all the packages are installed, you’ll need to configure *both* Gaufrette and Vich. This is where the documentation broke down a bit for me. You’ll need your Amazon AWS “Access Key ID” and “Secret Key” which are both available at https://portal.aws.amazon.com/gp/aws/securityCredentials if you’re logged into AWS.

Configure It

Once everything is configured at the YAML level, the final step is adding the Vich annotations to your entities.

Make sure you add the “@Vich\Uploadable” annotation to your Entity or Vich will fail silently.

The “mapping” specified in “@Vich\UploadableField(mapping=”logo”, fileNameProperty=”logo”)” needs to match the value under “vich_uploader.mappings” which you defined in config.yml

Finally, one last “gotcha” to be cognizant of is this bug – https://github.com/dustin10/VichUploaderBundle/issues/123. Since Vich uses Doctrine lifecycle callbacks to manage files, if no Doctrine fields are changed then the Vich code isn’t executed. The easiest way to get around this (and what we used), is just to manually update the “updated_at” column every time a form is submitted to ensure that the upload handling code is executed.

Anyway, as always, questions and comments are welcome.