Debugging By Dumping

By now, most of us will likely have used the dump($var); function made available for us in Symfony 2.6.

If you haven’t, or you think you can’t because you a) don’t have the latest and greatest version of Symfony, or b) don’t use Symfony, then never fear, you can include the VarDumper component in any project. No excuses.

Whilst the  dump($var); function is incredibly useful, in one instance so far, it has caused me more headaches than it has solved.

The Situation

I’m working on a new RESTful API using Symfony 2 as my back end.

Along the way, I want to restrict Users from being able to access other peoples data, for obvious reasons.

Without getting too deep into the code – as it’s really not that important to this problem / solution – I hit upon an issue where I wasn’t getting the expected output. Such is life of a developer.

However, I can tell you are getting twitchy from having not yet seen any code, so to placate your code cravings, here is the method:

    /**
     * @return array
     */
    public function findAll()
    {
        $accounts = $this->repository->findAll();

        foreach ($accounts as $account) {
            $this->denyAccessUnlessGranted('view', $account);
        }

        return $accounts;
    }

See, I told you, not much to look at.

At some point along the way (and in truth, I haven’t figured out exactly where as of yet), I know that $this->denyAccessUnlessGranted(‘view’, $account);  is having a bad time.

To begin with, I wanted to see exactly what the contents of $accounts was before it even hit the foreach loop.

To the dumper!

    /**
     * @return array
     */
    public function findAll()
    {
        $accounts = $this->repository->findAll();

        exit(dump($accounts));

        foreach ($accounts as $account) {
            $this->denyAccessUnlessGranted('view', $account);
        }

        return $accounts;
    }

Alas, no:

the-output-of-symfony-vardumper-dump

The next thing you might think is – ok, just use print_r($var); and be done with it.

Unfortunately, that’s not an option when dealing with most Doctrine entities. If you try, you may get lucky, you may hit funky maximum nesting level errors, or worse, you may crash your DHC client. Guess which I encountered?

My Solution

Never the less, there is a solution. Quite an old school solution. Something I used to use all the time before the VarDumper component was a thing:

exit(\Doctrine\Common\Util\Debug::dump($yourVariableHere));

This will give you an equivalent of print_r($var); but without the nasty blow ups:

the-output-of-doctrine-dump

It’s an old technique sir, but it checks out.

Now just to figure out why the test is failing, and everything will be right with the world once more.

Renaming Routes in DunglasApiBundle

Let’s say you have defined a new Resource  entry for your SocialMediaAccount  Entity:

# app/config/services.yml

    resource.social_media_account:
        parent:    "api.resource"
        arguments: [ "AppBundle\\Entity\\SocialMediaAccount" ]
        tags:      [ { name: "api.resource" } ]

Now, when we run php app/console debug:router we see the following:

dunglas-api-bundle-rename-routes

Ack. Underscores in our URLs.

Most likely not what we want.

Thankfully, changing this is really easy:

# app/config/services.yml

    resource.social_media_account:
        parent:    "api.resource"
        arguments: [ "AppBundle\\Entity\\SocialMediaAccount" ]
        calls:
            - method: "initShortName"
              arguments: [ 'social-media-account' ]
        tags:      [ { name: "api.resource" } ]

Success:

dunglasapibundle-custom-route-names

But, how did I know to add in that specific method in the calls block?

This line: parent: “api.resource” tells us that we are using a Parent Service.

Parent services are something of a trade off.

They decrease the amount of configuration you are required to write, but increase the technical complexity of comprehending the configuration.

The idea is that if we have two or more classes that are being configured as Symfony Services, and that they share identical setup requirements, then we can extract the common / shared requirements to a parent class, and tell our child services to use the configuration from the parent.

We can now remove the duplicated service setup / configuration.

However, newer or less experienced developers who read our code / configuration will have a harder time understanding what is happening.

But back to DunglasApiBundle!

We are using api.resource as our Parent service. This service definition must live somewhere, and as Symfony Bundles tend to follow a common structure, the best place to start looking would be in:

/vendor/dunglas/api-bundle/Resources/config/

There’s a fair few files in here:

dunglas-api-bundle-resources

If unsure, do a quick search for ‘api.resources’ in that folder, or resort to the old school method of opening them one-by-one and eye balling the service definitions until you find the one you want.

In our case, it lives inside api.xml , nice and easy:

<service id="api.resource" class="Dunglas\ApiBundle\Api\Resource" public="false" abstract="true" lazy="true" />

Now we have the class name so we know exactly where to look.

With the file found (/vendor/dunglas/api-bundle/Api/Resource.php ), all we need to know are the available method names. Thankfully, PHPStorm can provide us with a nice list of them:

dunglas-api-bundle-resource-php-methods

Sorry about the truncating. If you want to see that view then hit cmd / ctrl + 7, and then cmd / ctrl + 1 to return to ‘normal’ when you are done.

Now we have our list of methods, we can simply add the required methods, along with their parameters to our calls block in our concrete Resource setup:

# app/config/services.yml

    resource.social_media_account:
        parent:    "api.resource"
        arguments: [ "AppBundle\\Entity\\SocialMediaAccount" ]
        calls:
            - method: "initShortName"
              arguments: [ 'social-media-account' ]
        tags:      [ { name: "api.resource" } ]

Be sure to read up on Symfony Parent Services to understand this further.

Happy Dunglas’ing! 🙂

Adding Tumblr to HWIOAuthBundle

Tumblr HWIOAuthI recently needed to add in Tumblr social authentication to a Symfony project. I made use of the excellent HWIOAuthBundle, which made the whole process a breeze.

Unfortunately, out of the box, there is no direct support for Tumblr.

However, no fear, as manually adding support in is simple enough.

Follow the installation instructions through to the end of Step 2. At the bottom of the section on Configuring Resource Owners is the option for ‘Others‘.

To get this to work, it’s mainly a bit of back and forth between the Tumblr API docs and your config.yml file, so let me spare you the time:

hwi_oauth:
    # list of names of the firewalls in which this bundle is active, this setting MUST be set
    firewall_name: main # important to change this to match the firewall you are adding hwioauth too
    resource_owners:
        tumblr:
            type:                oauth1
            client_id:           your_tumblr_api_client_id_here
            client_secret:       your_tumblr_api_client_secret_here
            access_token_url:    https://www.tumblr.com/oauth/access_token
            authorization_url:   https://www.tumblr.com/oauth/authorize
            request_token_url:   https://www.tumblr.com/oauth/request_token
            infos_url:           https://api.tumblr.com/v2/user/info
            scope:               "read"
            user_response_class: HWI\Bundle\OAuthBundle\OAuth\Response\PathUserResponse
            paths:
                identifier:     response.user.name
                nickname:       response.user.name
                realname:       response.user.name

The paths section is interesting.

We must specify at least these three for HWIOAuthBundle to not error out.

What I’m doing here is using a dotted array access format. Kinda confusing sounding, but really what this is saying is : $response[‘user’][‘name’] .

Why the same data for each field?

Well, Tumblr doesn’t return much in the way of account data here.

You can see the full output from Tumblr here:

PathUserResponse.php on line 122:
array:2 [▼
  "meta" => array:2 [▶]
  "response" => array:1 [▼
    "user" => array:5 [▼
      "name" => "futuristicallycrookedbread"
      "likes" => 0
      "following" => 6
      "default_post_format" => "html"
      "blogs" => array:1 [▼
        0 => array:29 [▼
          "title" => "Untitled"
          "name" => "futuristicallycrookedbread"
          "posts" => 4
          "url" => "http://futuristicallycrookedbread.tumblr.com/"
          "updated" => 1441562102
          "description" => ""
          "is_nsfw" => false
          "ask" => false
          "ask_page_title" => "Ask me anything"
          "ask_anon" => false
          "followed" => false
          "can_send_fan_mail" => true
          "is_blocked_from_primary" => false
          "share_likes" => true
          "likes" => 0
          "twitter_enabled" => false
          "twitter_send" => false
          "facebook_opengraph_enabled" => "N"
          "tweet" => "N"
          "facebook" => "N"
          "followers" => 0
          "primary" => true
          "admin" => true
          "messages" => 0
          "queue" => 0
          "drafts" => 0
          "type" => "public"
          "subscribed" => false
          "can_subscribe" => false
        ]
      ]
    ]
  ]
]

This is the result of using Symfony’s VarDumper component on line 122 of PathUserResponse.php .

How did I know to add the dump($response); statement in there?

Well, we specified in the config.yml file that we are expecting a User Response to be:

user_response_class: HWI\Bundle\OAuthBundle\OAuth\Response\PathUserResponse

I just opened up that file and added in the dump, using the $response variable that had been defined earlier on line 113.

There is a little bit of extra config in security.yml :

security:
    providers:
        social_user_provider:
             id: oauth_user_provider

    firewalls:
        main:
            anonymous: ~
            oauth:
                resource_owners:
                    tumblr:        "/login/check-tumblr"
                login_path:        /login
                use_forward:       false
                failure_path:      /login
                oauth_user_provider:
                    service: oauth_user_provider

    access_control:
        - { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/connect, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/, role: ROLE_USER }

Alongside the other HWIOAuth routes, you will need to add in one for Tumblr:

# app/config/routing.yml - or where ever you are storing routes
tumblr_login:
    path: /login/check-tumblr

The oauth_user_provider  service must exist, so here is my definition:

# services.yml (or where ever)
services:
    oauth_user_provider:
        class: AppBundle\Model\OAuthUserProvider

This should work for other social auth providers, with only a little modification. As much as this looks like loads of config (hey, it’s a Symfony project!), it’s actually really pretty easy to add in new social providers. I was pleasantly surprised.