If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
If I give you an object, could you print it? What I mean is, in battle.php
, after
we determine the winners, we echo $ship1->getName()
, which is of course a string:
... lines 1 - 39 | |
<html> | |
... lines 41 - 59 | |
<body> | |
<div class="container"> | |
... lines 62 - 64 | |
<div> | |
<h2 class="text-center">The Matchup:</h2> | |
<p class="text-center"> | |
<br> | |
<?php echo $ship1Quantity; ?> <?php echo $ship1->getName(); ?><?php echo $ship1Quantity > 1 ? 's': ''; ?> | |
VS. | |
<?php echo $ship2Quantity; ?> <?php echo $ship2->getName(); ?><?php echo $ship2Quantity > 1 ? 's': ''; ?> | |
</p> | |
</div> | |
... lines 74 - 108 | |
</div> | |
</body> | |
</html> |
But could we just print $ship1
and $ship2
?
... lines 1 - 39 | |
<html> | |
... lines 41 - 59 | |
<body> | |
<div class="container"> | |
... lines 62 - 64 | |
<div> | |
<h2 class="text-center">The Matchup:</h2> | |
<p class="text-center"> | |
<br> | |
<?php echo $ship1Quantity; ?> <?php echo $ship1; ?><?php echo $ship1Quantity > 1 ? 's': ''; ?> | |
VS. | |
<?php echo $ship2Quantity; ?> <?php echo $ship2; ?><?php echo $ship2Quantity > 1 ? 's': ''; ?> | |
</p> | |
</div> | |
... lines 74 - 108 | |
</div> | |
</body> | |
</html> |
Does it make sense to print an object? The answer is... no. Try to battle, you get a very clear error that says:
Object of class
Model\RebelShip
could not be converted to string inbattle.php
.
Remember this error: you'll eventually try to print an object on accident and see this!
Why am I telling you this seemingly small and obvious fact? Because I'm lying! You can print objects! You just have to do a little bit more work.
Here's the big picture: there are ways to give a class super-powers - like the ability to be printed or - as we'll see next - the ability to pretend like it's an array.
Open up AbstractShip
. To make objects of this class printable, go to the bottom
and create a new public function __toString()
. Inside, return $this->getName()
:
... lines 1 - 4 | |
abstract class AbstractShip | |
{ | |
... lines 7 - 124 | |
public function __toString() | |
{ | |
return $this->getName(); | |
} | |
} |
Go back, refresh, and now it works just fine.
By adding the __toString()
method - we gave PHP the ability to convert our object
into a string. The __toString()
must be called exactly like this, and there are other
methods that take on special meaning. They all start with __
, and we've already
seen one: __construct()
:
... lines 1 - 4 | |
abstract class AbstractShip | |
{ | |
... lines 7 - 29 | |
public function __construct($name) | |
{ | |
$this->name = $name; | |
} | |
... lines 34 - 128 | |
} |
These are collectively called Magic Methods.
There are actually just a few magic methods: let's look at another common one.
In battle.php
, scroll down a little bit to where it shows the ship health. Change
this: instead of $ship1->getStrength()
, say $ship1->strength
:
... lines 1 - 39 | |
<html> | |
... lines 41 - 59 | |
<body> | |
<div class="container"> | |
... lines 62 - 73 | |
<div class="result-box center-block"> | |
... lines 75 - 95 | |
<dl class="dl-horizontal"> | |
... line 97 | |
<dd><?php echo $ship1->strength; ?></dd> | |
... lines 99 - 100 | |
</dl> | |
</div> | |
... lines 103 - 108 | |
</div> | |
</body> | |
</html> |
This should not work, and PHPStorm tells us why: the member - meaning property -
has private access. We can't access a private
property from outside the class.
But once again - via a magic method - you can bend the rules. This time, add a
public function __get()
with a single argument: $propertyName
. For now, just
dump that:
... lines 1 - 4 | |
abstract class AbstractShip | |
{ | |
... lines 7 - 129 | |
public function __get($propertyName) | |
{ | |
var_dump($propertyName);die; | |
} | |
} |
Refresh to see what happens. Interesting! It dumps the string strength
. Here's
the magic: if you reference a property on your object that is not accessible - either
because it doesn't exist or is private or protected - and you have an __get()
method, then PHP will call that and pass you the property name.
Then - if you want - you can return its value. Add return $this->$propertyName
:
... lines 1 - 4 | |
abstract class AbstractShip | |
{ | |
... lines 7 - 129 | |
public function __get($propertyName) | |
{ | |
return $this->$propertyName; | |
} | |
} |
This looks weird: PHP will see $propertyName
, evaluate that to strength
, and
then return $this->strength
.
Refresh again. It works!
Not surprisingly, there's also a method called __set()
, which allows you to assign
a value to a non-existent property, like $ship->strength = 100
.
Now, just because you have all this new power doesn't mean you should use it.
As soon as you add things like __get()
, it starts to break your object oriented
rules. All of a sudden, even though it looks like strength
is private, I actually
can get it... so it's not really private.
You also won't get reliable auto completions from your editor - it has a hard time figuring out what you're doing in these magic methods.
So my recommendation is: avoid using magic methods, except for __toString()
and
__construct()
.
But, you do need to know these exist: even if you don't use them, other libraries will, which might be confusing if you're not watching for it.
But beyond magic methods, there are other super powers you can give your objects that I do love. Let's look at those.
Hey!
I get confused with magic methods. I don't get the logic behind it, for example how does $propertyName equels strength I get the __construct() but the rest is confusing.
Hey Emin,
About magic get() method: if your class has get() method - every time you when you call a property that does not exist or does not public - before throwing an error, PHP interpreter will call that __get() method and pass a property name to it that you're trying to access. So, if you have this code in your class:
public function __get($propertyName)
{
return $this->$propertyName;
}
Every time you call any property, for example, "$object->test" - the __get() method will be called and "test" string (the property name you're trying to call) will be passed as an argument, i.e. $propertyName variable will be equal to "test". And then, in that method, we call "return $this->$propertyName;" but since $propertyName = "test" it means you just call "return $this->test;" - i.e. you call property name directly.
So, basically, you need to understand the difference between "$this->propertyName;" and "$this->$propertyName;" - that's very important and I bet you confused because of it. So, you always call properties like "$this->propertyName;" inside your class, but "$this->$propertyName;" is also a legitimate syntax that means PHP will interpret the $propertyName variable first and then call a property that equal to that variable's value, for example:
$propertyName = 'foo';
$this->$propertyName; // is the same as calling "$this->foo", because $propertyName is equal to "foo" sting above.
I hope it clear for you now.
Cheers!
Hey victor,
Thank you for the fast reply. Now It makes more sense on how it works and with experience I will understand it allot better.
Cheers!
Hi. I know these are rather old courses, still, I think someone messed with the challenge files, as a lot are broken.
I think it would be great to not include these coding challenges and instead replace them with questions as other challenges are, so no one gets to mess with them and the courses can be completed.