Comments

Static and Instance Method Calls.

PHP does not differentiate between methods called on instantiated classes and un-instantiated classes like Ruby does.

Take the following code for example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Foo

  # Class Method. can be called like this:
  # Foo::bar() or Foo.bar()
  def self.bar
    puts 'class method'
  end

  # Instance Method, can be called like this:
  # Foo.new.bar
  def bar
    puts 'instance method'
  end

end

Calling these will get the following result:

1
2
3
4
2.0.0-p247 :001 > Foo.bar
class method
2.0.0-p247 :002 > Foo.new.bar
instance method

In Ruby, using this code is completely legal. Ruby knows that class methods and instance methods are separate and namespaces them accordingly. This allows class and instance methods to have the same name (provided we put self. in front of a class definition or extend self within the object).

Similar code can be expressed like this in PHP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class Foo {

  // Static, can be called like this:
  // Foo::bar();
  public static function bar() {
    echo 'static';
  }

  // Instance, can be called like this:
  // $f = new Foo();
  // $f->bar();
  public function bar() {
    echo 'instance';
  }
}

Above we use the similar PHP concept of a static method (similar to class methods in that they can be called on non instantiated classes) and an instance method. Like the above Ruby code we gave them the same name.

One would expect that PHP would function similar to Ruby in that it will keep it’s static references separate.

However, this will cause PHP to throw a fatal error:

1
PHP Fatal error:  Cannot redeclare Foo::bar()

This is because of backward compatibility reasons. PHP 5 allows instance methods to be called on an a non-instantiated class. This can be a bit frustrating if you are coming from Ruby as the ‘::’ operator acts slightly different.

For example: If you wish to create a class called File. Perhaps you would like two similar methods that check if a file is writable. One you can pass a file name to, and the other that when called on an instantiated File object won’t need a file name – it will just reference the current object.

There are other ways to get PHP objects to act like Ruby objects and call static/instance methods separately using Object Overloading, but this is a hack and should be avoided. The rule of thumb in PHP is just to treat every method as an instance method. If you are dead set on using both static and instance methods in your PHP code, just pay extra mind to the following:

  • Instance methods can be called statically: Check for the existence of $this.
  • Instance methods and static methods can’t have the same name.
  • __call() and __callStatic() are your best friends.

Edit: What I had here before was not technically correct. Ruby actually does not have static methods. Ruby has Class Methods. These can be referenced in a ‘static’ manner (that is, on a class rather than an object). In order to keep the post to the point, I unintentionally over simplified the differences between the two languages’ design patterns.

I’ve updated the language a bit to make the difference obvious.

Comments

Yesterday my younger brother turned 21. I’m not a big fan of buying Hallmark cards so this will have to suffice.

Happy Birthday Owen! Proud of you kid.

Comments

There has been an increase in a certain mentality in development society. Instead of writing a test to ensure functionality, we write a ton of tests that tell us things that tests, in reality, aren’t designed to tell us about. These tests take the place of the far more reliable and less application inclusive practice of debugging.

Tests tell us something is wrong. Debugging tells us what is wrong and how to fix it.

What should I be testing and what should I be debugging?

It’s really a matter of good judgement. Let me give an example:

1
2
3
4
5
class Ball > ActiveRecord::Base
    def radius
        length / 2
    end
end
1
2
3
4
5
6
describe Ball do
    it "should calculate the radius" do
        subject.length=12
        subject.radius.should eq 6
    end
end

This is great. One assertion on a public method. Now let’s calculate the circumference.

1
2
3
4
5
6
7
8
9
10
11
class Ball > ActiveRecord::Base
    def circum
        2*3.14*self.radius
    end

    private

    def radius
        length / 2
    end
end
1
2
3
4
5
6
7
8
9
10
11
describe Ball do
    it "should calculate the circum" do
        subject.length=12
        subeject.send(:radius).should eq 6
        subject.circum.should eq 37.68
    end
    it "should calculate radius"
        subject.length=8
        subject.send(:radius).should eq 4
    end
end

Woah, why? We already tested radius in the diameter test. No need to Write Everything Twice (WET). By the time this application scales and reaches 4000 of these suckers it’s going to be hell.

Even if radius failed to return the expected result, we would be alerted to it when cirum failed. So why have two failing tests? Exclusively so we can know which part of the model is failing? That’s not the test’s job. The test is there simply to say “Hey, something is wrong in Ball model I’m not getting the expected result on cirum, you must have broken something in your last edit.”

1
2
3
4
5
6
describe Ball do
    it "should calculate the circum" do
        subject.length=12
        subject.circum.should eq 37.68
    end
end

Much better. We know cirum relies on radius. This test will fail if either of the methods logic fails to produce an expect result.

All you need is one test, with one assertion, that checks everything within the scope.

So what if circum fails to return the expected result? How will I know where to look?

Look in circum and the methods it’s dependent on. Don’t be lazy. Just because you can write tests on radius doesn’t mean you should. Why write a tests that slows down you’re suite and provides no additional benefit other than to keep you out of the debugger? There are tools much more suited for this.

The only reliable method to finding and fixing a problem is to debug it.

So let’s say the above test fails.

1
Failure: circum expected to return 37.68 and got nil

Always work from the bottom up. Radius is the first place to start ( the lowest call in the stack that we have written. )

1
2
3
def radius
    length / 2
end

Nothing seems wrong there. Let’s go a level lower.

1
2
[2] pry(main)> length
=> nil

Oh silly us! We forgot to set the length. There’s our problem. A nice quick fix and the test is passing again! awesome!

But, why not?

Specs should do one thing ( and they do it very well I might add ). Tell you something is wrong. Nothing more. Writing tests to debug for you is resource intensive. The application should not experience this overhead because you have tools to.

If you have Objects with an average 20 private methods and 10 public methods each that reference them for example. Assuming each additional test consumes about 2 ( controller/model specs can be less, request specs are much worse ) additional seconds to run, by the time you’ve written 300 of these tests you’ve added an additional 12,000 seconds ( 200 minutes ) to run the entire suite.

Any time you save by avoiding debugging will be wasted in testing.

Save time. Debug it.