PHP

2012 Recap

So I wanted to take a little time and document what happened in 2012 for myself.

I think the biggest things I want t take away from 2012 are a few key lessons:

Don’t Be Afraid of What Other’s Think

If you spend your life mired in worries about what others will think of what you are doing, you will never get anything done.

Just do.

Too much of our lives (collectively) are spent wondering about what others will think about what we are doing? So what. Who CARES if you are wrong. People are wrong ALL OF THE TIME. What matters is that you are not so attached to your wrong opinion that you don’t change it when presented a valid argument otherwise.

It’s said the most ‘thought to to be smart’ people aren’t smart because they’re right but because they are quick to change their minds.

I, of course, have the luxury of never particularly caring what others have thought of what I say or do. It’s lead to some pretty awful predicaments — but some pretty awesome ones too. Who, among my close friends, would have pictured me as being one of the ones to immediately volunteer for Symfony Jeopardy at Symfony Live? Especially when I am culturally dumb and (at time time) was quite technically deficient. That didn’t stop me from answering the only then-to unanswered question though!

Just do.

Go up in front of everyone, be a goof ball, have fun, laugh, be silly. Anyone who actually cares in a bad way isn’t anyone you want to associate with anyway.

Just do.

Don’t be afraid of what others will think. Seriously. Most commonly, what most other people will be saying is “Gee, I wish I could do that!” And you know why they can’t? BECAUSE THEY ARE AFRAID! 99% of the people who would say anything negative would never have the nerve to do anything like what you are doing. It’s 100000x easier to criticize than to produce. Be sure to check the background of those who give you negative feedback and take their input with the appropriate level of expertise it warrants.

Just do.

Get out in the community

The PHP Community in general is AMAZING. It’s full of brilliantly smart people you can talk to, and gives you external input to your internal processes. If your local community isn’t as as deep into the things you are? Learn to teach them! Learn about how to talk to other developers, how to share your skills and techniques. You’d be surprised what you’re capable of.

If you haven’t spoken to groups before, consider giving a few talks to a local user group. Most likely they’re running their regular speaker lineup through the wringer, week after week, and offering to talk about something (anything!) that you know even passingly well enough to talk about (I once essentially winged a talk about design patterns — shhhh!).

The other thing being in the community will do is give you a feel for the pulse of where things are heading. There are currents and drafts in the community which you don’t catch unless you’re paying attention. See what’s on the horizon, and pay attention. You’ll never know what you’ll miss if you’re not paying attention.

Prove you can ship

I can’t reiterate this enough. The value of someone who can actually ship a product is waaaaay above someone who can only point to minor contributions in a larger project. If you’re one of those nameless cogs in a giant organization, it’s time to join an open source project or build your own thing on the side. Seriously. Nothing shows your value more than being able to to actually ship something. In the world of business, ideas are cheap, and execution is everything. Show people you can execute, and you will open doors.

Know your stuff

Read up on the important things core to being a high quality software developer: Patterns, Anti-Pattens, and Code Smells, among other things. These will teach you the do’s and don’ts of our industry, our trade. They will show you what to do, and often more importantly, what not to do. It’s the same as new inductees into Martial Arts learn Kata’s, new developers need to learn these patterns, anti-patterns, and code smells. It gives you the tools to reflexively approach similar problems in the future, and tools to expand on when you need to create something out of thin air.

Don’t be afraid to be wrong

Look at my blog, or the advice I give out in #symfony — I’m “wrong” all of the time. There’s usually a much more efficient way to do something than I recommend — but I don’t know it! I help people get stuff done. The academic stuff can be left for those Ivory Tower folk. I’m much more interested in what gets stuff done in the trenches. Getting something done is more often much more important than doing it in the best possible way. Working implementations refactor much quicker than potential implementations grow while wandering in the sea of WTF I HAVE NO IDEA WHAT I AM DOING?!. Being wrong is not a bad thing. It means both that you were strong enough in your opinion that you could voice it, and that when presented with solid contrary evidence, you changed your wrong opinion into a right one. How awesome is that?!

Enjoy what you do

We’re in this because we have a passion for either building better code, or building things people love to use, or hell, even just building things. But if you’re not enjoying your work — seriously the door is right over there. Life is FAR TOO SHORT to spend your days doing things you loathe. If you’re going to do something for your career, make sure it’s something you enjoy. I avoided doing development as an actual full career for many many many many many many many many years, because I was afraid that if I took what I loved and made it a job, it would never be the same. Interestingly, I was right — it was never the same. Now I love going to “work” almost every single day. Seriously, it’s that simple. Do what you love and you never work a day in your life. Honest.

Now with that out of the way

You may be asking yourself “But this is a recap — what do YOU do during this year?” Well, let’s see:

  1. I went to Symfony Live.
  2. I started blogging more regularly.
  3. I was more active in #symfony, #silex, #phpmentoring, #protalk, and #symfony-dev (yikes, right?).
  4. I contributed to the Symfony Documentation.
  5. I started attending User Groups regularly (in Lansing, and Ann Arbor).
  6. I gave a talk in front of 100+ people.
  7. I gave a talk over 30+ minutes.
  8. I have at least one person using one of my open source bundles for Symfony2
  9. I have given three people technical feedback on their technical products which have (I feel) reasonably improved the quality of their product (I think I have a knack for this — have sent an email to php|arch to be a technical reviewer — look for this in 2013’s recap!).
  10. I convinced my 4 best business friends to all be business friends together.
  11. I started mentoring officially (Uzo, Luis, and Yitzchok — you guys are the best folks a mentor could ask for, really!).

What do I want to do, going forward?

I’ll tell you what’s on my list.

  1. I want to launch a product (in the works)
  2. I want to give a mainstream talk at a conference (not an unconf talk)
  3. I want to be more involved in the community (you guys are awesome, seriously!)
  4. I want to help more people hate their jobs less (hey, it’s what I do…)
  5. I want to blog more (is this cliché?)

Anyway — 2012 was great, let’s make 2013 even better!

About the Decorator Pattern

The Decorator Pattern lets you to easily extend a given set of objects (either grouped via class inheritance or interface) and extend their functionality in a way that avoids needlessly duplicating code.

Our example today will hopefully be pretty close to form, and while generally contrived, will simply illustrate the use of the Decorator Pattern.

Let’s say for instance, you run a video rental service. Let’s call it… SmedRocks. Now your crazy programmers have already built your inventory tracking system as a web service, and you have no control over how they have implemented it, but you have to fight the good fight and soldier on. Features must be implemented. To make this even simpler, our API has one function:

getVideos – This returns all of the titles we have, no matter if they are currently rented or not.

Now getVideos returns a JSON array like so:

{
    {
        'title': 'Some Movie',
        'status': 'rented'
    },
    {
        'title': 'Some Other Movie',
        'status': 'available'
    }
}

I’m sure you can already see the problem with how it’s returning data. Rented movies and available movies are all mixed in! What a pain!

Ok, so let’s look at what we need to do to get started. Let’s make our API. This isn’t how you would ACTUALLY do this, it’s just an example to get us moving along:

class MovieApi
{
    public function getVideos()
    {
        return json_decode(file_get_contents('http://example.org/rest/getVideos.json'));
    }
}

Excellent! We can now get our videos, but wait — we’re in the middle of building this and example.org seems to have gone offline. This isn’t going to help us get this going! So let’s refactor real quick so we can keep going on other things.

interface MovieApiInterface
{
    public function getVideos();
}
 
class LiveMovieApi implements MovieApiInterface
{
    public function getVideos()
    {
        return json_decode(file_get_contents('http://example.org/rest/getVideos.json'));
    }
}
 
class OfflineMovieApi implements MovieApiInterface
{
    public function getVideos()
    {
        $json = '{{"title": "Movie 1", "status": "rented"},{"title": "Movie 2", "status": "available"}}';
        return json_decode($json);
    }
}

Look at that! We’ve now defined a central MovieApiInterface which both implementations conform to, and have both an online and offline implementation which will also ultimately make writing tests easier.

Ok, now for the juicy part: we need to be able to ask this system for just rented or available movies. We could extend each API implementation, but that’s going to be some duplication we can live without.

Enter the Decorator Pattern.

With the decorator pattern, we can build an object that accepts a MovieApiInterface object in it’s constructor, and provides a uniform higher-level way to interact with our lower-level API.

Again, this is basic code to get you thinking, not optimized production-ready code.

class SpecificMovieFinder implements MovieApiInterface
{
    protected $movie_api;
 
    public function __construct(MovieApiInterface $movie_api)
    {
        $this->movie_api = $movie_api;
    }
 
    public function getVideos()
    {
        return $this->movie_api->getVideos();
    }
 
    public function getRentedMovies()
    {
        $movies = $this->getVideos();
        $rented_movies = array();
 
        foreach ($movies as $movie) {
            if ($movie->status == 'rented') {
                $rented_movies[] = $movie;
            }
        }
 
        return $rented_movies;
    }
 
    public function getAvailableMovies()
    {
        $movies = $this->getVideos();
        $available_movies = array();
 
        foreach ($movies as $movie) {
            if ($movie->status == 'available') {
                $available_movies[] = $movie;
            }
        }
 
        return $available_movies;
    }
}

Now we have a simpler API that actually conforms to how we will use it in practice instead of how the API designers built the service. This is great! They get their way, and we get ours. Everyone wins.

So how do we use it?

// development configuration
$offline_api = new SpecificMovieFinder(new OfflineMovieApi());
 
// live configuration
$online_api = new SpecificMovieFinder(new LiveMovieApi());

Now we get the same functionality on multiple implementations, and as an added bonus, our SpecificMovieFinder also still implements the MovieApiInterface allowing us to use it interchangeably with any other service that may need our api down the line!

My good friend Beau Simensen pointed out that unfortunately, my example is a little deficient. I’ll let his gist do the talking:

Thanks Beau!

What are some other places you can think of using this pattern?

About the Proxy Pattern

Edit: Added an update with a more concrete example further down.

I just wanted to share and provide a further explanation on the Proxy Pattern example I gave at the Ann Arbor PHP/MySQL User Group today.

Source: https://gist.github.com/4302634

Now, Dan had mentioned that this could be done through simple inheritance instead of via interface contract, and after some thought, I realized that I disagree.

Typically with an API object, you will initialize with a remote URL, and perhaps a token:

public function __construct($api_url, $auth_token)

But remember our new Cache API proxy only takes an API object and a Cache object.

public function __construct($cache, $api)

This fundamentally changes the contract that the initial API object creates, and as such, should then NOT extend the original API implementation.

Part of doing OOP well involves the management of contracts we establish. Changing the rules of a function in a sub-classed entity violates the Liskov substitution principle (as referenced in SOLID object oriented design).

Now, you COULD subclass the MyApi class, and add the Cache control via setter injection, but personally I don’t think that implementation is nearly as clean as providing a proxy object to add the caching functionality.

 

p.s. I’ll leave QuickTime running but I think it did eat the recording of the presentation — sorry guys! I’ll put the slides up soon but I don’t know how much context they will provide given how light they were compared to the commentary that went along with it.

 

Update (12/07/2012):

It has been pointed out that my example was perhaps a little too contrived, so I think I found a better one.

Let’s say you’re building a system out, and you know you want logging, but you don’t know what sort of implementation you want to do for said logging. Given that instance, let’s start with our basic interface:

1
2
3
4
5
6
interface Logger
{
  public function info($message);
  public function debug($message);
  public function fatal($message);
}

Ok, great! So, because production schedules are tight, we decide to give a very basic implementation first:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyBasicLogger implements Logger
{
  public function info($message)
  {
    error_log('INFO: '.$message);
  }
 
  public function debug($message)
  {
    error_log('DEBUG: '.$message);
  }
 
  public function fatal($message)
  {
    error_log('FATAL: '.$message);
  }
}

Alright! We now have our basic logger implemented!

Oh, what’s that you say? You want to use Monolog in place of error_log() in production? sure!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyMonologLogger implements Logger
{
  protected $monolog;
 
  public function __construct($monolog)
  {
    $this->monolog = $monolog;
  }
 
  public function info($message)
  {
    $this->monolog->addInfo($message);
  }
 
  public function debug($message)
  {
    $this->monolog->addDebug($message);
  }
 
  public function fatal($message)
  {
    $this->monolog->addCritical($message);
  }
}

Now we have two ways of logging messages. Now comes the constraint that we want to be emailed when fatal errors are logged. Your first instinct would be to extend MyMonologLogger and add it by overloading fatal(), but then we can’t use that functionality on any other logger we ever build. How do we build this functionality in a way that we can reuse over and over again? The Proxy pattern.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MyEmailingLoggerProxy implements Logger
{
  protected $logger;
 
  public function __construct(Logger $logger)
  {
    $this->logger = $logger;
  }
 
  public function fatal($message)
  {
    $this->logger->fatal($message);
    mail([email protected]', 'A fatal error has occurred', $message);
  }
 
  public function info($message)
  {
    $this->logger->info($message);
  }
 
  public function debug($message)
  {
    $this->logger->debug($message);
  }
}

And now, no matter what we choose as our logging backend, now or in the future, we will always easily be able to have those fatal errors emailed to us by simply putting our chosen logging system inside an instance of MyEmailingLoggerProxy.

I hope this clears some things up!

twitter