Introduction
When working with PHP code, you may often come across parent::
, static::
, and self::
. But when you're first starting out as a developer, it can sometimes be confusing to know what they do and the differences between them.
I'll hold my hand up and admit that for quite a long time after I first started as a developer, I thought static::
and self::
were the exact same thing.
So in this article, we're going to cover what each of these can be used for and the differences between them all.
What is parent::
?
Let's start off with talking about parent::
.
To get an idea of what it does, we're probably best off with looking at some code examples first.
Let's imagine that we have a BaseTestCase
class that has a setUp
method:
1class BaseTestCase 2{ 3 public function setUp(): void 4 { 5 echo 'Run base test case set up here...'; 6 } 7} 8 9(new BaseTestCase())->setUp();10 11// Output is: "Run base test case set up here...';
As we can see, when we call the setUp
method, it runs as expected and outputs the text.
Now, let's imagine that we want to create a new FeatureTest
class that inherits the BaseTestCase
class. If we wanted to run the setUp
method of the FeatureTest
class, we could do so like this:
1class FeatureTest extends BaseTestCase2{3 //4}5 6(new FeatureTest())->setUp();7 8// Output is: "Run base test case set up here...";
As we can see, we haven't defined the setUp
method in our FeatureTest
, so the one defined in the BaseTestCase
will be run instead.
Now, let's say that we want to run some extra logic when we run the setUp
method in our FeatureTest
. For instance, if these classes were test cases that were being used as part of a PhpUnit test, we may want to do things such as creating models in the database, or setting test values.
At first, you might (incorrectly) think that you can just define the setUp
method in your FeatureTest
method and call $this->setUp()
. I'll be honest, I always used to fall into this trap when I was first learning about programming!
So our code may look something like this:
1class FeatureTest extends BaseTestCase 2{ 3 public function setUp(): void 4 { 5 $this->setUp(); 6 7 echo 'Run extra feature test set up here...'; 8 } 9}10 11(new FeatureTest())->setUp();
But, you'll find that if we were to run this code, we'd end up in an loop that would cause your application to crash. This is because we're recursively asking setUp
to call itself over and over. You'd likely get an output similar to this:
1Fatal error: Out of memory (allocated 31457280 bytes) (tried to allocate 262144 bytes) in /in/1MXtt on line 1523mmap() failed: [12] Cannot allocate memory45mmap() failed: [12] Cannot allocate memory67Process exited with code 255.
So instead of using $this->setUp()
, we need to tell PHP to use the setUp
method in the BaseTestCase
instead. To do this, we can replace $this->setUp()
with parent::setUp()
like so:
1class FeatureTest extends BaseTestCase 2{ 3 public function setUp(): void 4 { 5 parent::setUp(); 6 7 echo 'Run extra feature test set up here...'; 8 } 9}10 11(new FeatureTest())->setUp();12 13// Output is: "Run base test case set up here... Run extra feature test set up here...";
Now, as you can see, when we run the setUp
method in the FeatureTest
class, we're first running the code in the BaseTestCase
and then continuing with the rest of the code defined in our child class.
It's worth noting that you don't always need to place the parent::
call at the top of the method. In fact, you can place it in the method wherever you'd like that best fits the purpose of the code. For example, if you wanted to run your code in the FeatureTest
class first then the BaseTestCase
, you could move the parent::setUp()
call to the bottom of the method like so:
1class FeatureTest extends BaseTestCase 2{ 3 public function setUp(): void 4 { 5 echo 'Run extra feature test set up here...'; 6 7 parent::setUp(); 8 } 9}10 11(new FeatureTest())->setUp();12 13// Output is: "Run extra feature test set up here... Run base test case set up here...";
What is self::
?
Now, let's take a look at self::
.
Let's imagine that we have a Model
class that has a static connection
property and a makeConnection
method. We'll also imagine that we have a User
class that inherits the Model
class and overrides the connection
property.
The two classes may look something like so:
1class Model 2{ 3 public static string $connection = 'mysql'; 4 5 public function makeConnection(): void 6 { 7 echo 'Making connection to: '.self::$connection; 8 } 9}10 11class User extends Model12{13 public static string $connection = 'postgres';14}
Now let's run the makeConnection
method on both classes and see what output we'd get:
1(new Model())->makeConnection();2 3// Output is: "Making connection to mysql"4 5(new User())->makeConnection();6 7// Output is: "Making connection to mysql";
As we can see, both calls resulted in the Model
class' connection
property being used. This is because self
uses the property that is defined on the class where the method exists. In both cases, the makeConnection
method is on on the Model
class because one doesn't exist on the User
class.
To further show this, we'll add the makeConnection
method to our User
class like so:
1class Model 2{ 3 public static string $connection = 'mysql'; 4 5 public function makeConnection(): void 6 { 7 echo 'Making connection to: '.self::$connection; 8 } 9}10 11class User extends Model12{13 public static string $connection = 'postgres';14 15 public function makeConnection(): void16 {17 echo 'Making connection to: '.self::$connection;18 }19}
Now if we were to call both methods again, we'd get the following output:
1(new Model())->makeConnection();2 3// Output is: "Making connection to mysql"4 5(new User())->makeConnection();6 7// Output is: "Making connection to postgres";
As you can see, the call to makeConnection
would now use the connection
field on the User
class because that's what where the method exists.
What is static::
?
Now that we have an idea of what self::
does, let's take a look at static::
.
To better understand what it does, let's update our code example above to use static::
instead of self::
like so:
1class Model 2{ 3 public static $connection = 'mysql'; 4 5 public function makeConnection() 6 { 7 echo 'Making connection to: '.static::$connection; 8 } 9}10 11class User extends Model12{13 public static $connection = 'postgres';14}
If we were to run the makeConnection
method on both classes, we'd get the following output:
1(new Model())->makeConnection();2 3// Output is: "Making connection to mysql"4 5(new User())->makeConnection();6 7// Output is: "Making connection to postgres";
As we can see, this output is different to when we used self::$connection
earlier. The call to the makeConnection
method on the User
class has used the connection
property on the User
class rather than the Model
class (where the method actually belongs). This is due to a feature in PHP called "late static binding".
If you're interested in reading more about late static binding, you can check out the PHP documentation here.
According to the PHP docs:
"This feature was named "late static bindings" with an internal perspective in mind. "Late binding" comes from the fact that static:: will not be resolved using the class where the method is defined but it will rather be computed using runtime information. It was also called a "static binding" as it can be used for (but is not limited to) static method calls."
So in the case of our example, the connection
property on the User
class is used because we're calling the makeConnection
method on that very same class.
However, it's worth noting that if the connection
property didn't exist on the User
class, it would fallback to using the one on the Model
class instead.
When to use "self::" or "static::"?
Now that we have a general idea of the difference between self::
and static::
, let's quickly cover how to decide which of these to use in your own code.
It all really comes down the use-case of the code that you're writing.
In general, I'd typically use static::
instead of self::
because I'd want my classes to be extendable and provide support for if they're inherited from.
For example, let's say that I want to write a class that I fully intend to be inherited from by a child class (such as the BaseTestCase
class in our example above). Unless I really wanted to prevent the child class from overriding the property or method, I'd want to use static::
.
It would mean that I could have confidence that if I override any of the static methods or fields that my child class would use my overrides. I can't tell you how many times I've run into bugs in my code when I've used self::
in a parent class and then couldn't figure out why my child class wasn't using my override!
On the flip side, some developers may argue that you should stick to using self::
because you shouldn't really be inheriting from classes. They might suggest that you should be following the principle of "composition over inheritance" instead. I won't delve too much into this topic because that's for another blog post in the future. But in broad, simple terms, this principle states that you should avoid adding functionality to your classes by putting all your logic in a parent class, and instead add the functionality by building your class up with lots of smaller classes.
This means if you followed this principle, you wouldn't need to use static::
because you wouldn't ever be extending your parent class. If you wanted to ensure that the class couldn't be extended, you could even take the code one step further and use the final
keyword when defining a class. Using the final
keyword prevents a class from being inherited from, so it could reduce any worry in your mind that the class might accidentally be extended and introduce any potential bugs.
In general, it's usually best for you to decide on a case-by-case basis at the time of writing your code whether you should use static::
or self::
.
Conclusion
Hopefully, this post should have given you an insight into the difference between static::
, self::
, and parent::
.
If you enjoyed reading this post, I'd love to hear about it. Likewise, if you have any feedback to improve the future ones, I'd also love to hear that too.
You might also be interested in checking out my 220+ page ebook "Battle Ready Laravel" which covers similar topics in more depth.
If you're interested in getting updated each time I publish a new post, feel free to sign up for my newsletter below.
Keep on building awesome stuff! 🚀