Confessions of a Converted PHP Developer: On Visibility and Privates
Alright class—today I’m here to talk about the differences and similarities that PHP and Ruby have when it comes to object properties, methods, and their visibility—how you create variables inside classes, and how you can interact with them.
A Quick Recap of What We Know
In PHP, an object’s properties can be set to one of three visibilities; public
, protected
, and private
. Properties that don’t have a visibility declaration default to public. Each of these declarations change what information is available to the class itself, its parents and children objects, and other code interacting with it. Let’s recap on how they work quickly.
- Public variables are available everywhere
- Protected variables are available only inside the class and its parents or children
- Private variables are only available in the class where they are specified, and are not available to classes that extend it
To handle interactions with protected and private properties it’s common to create getter and setter accessor methods. These are functions that allow you to set and retrieve the values of properties, and generally all look pretty similar—unless they’re actually doing manipulation to the data that’s stored, it’s common to see them as just single line functions returning or setting the property:
<?PHP
class animal
{
private $_species;
protected $_name;
public function get_species()
{
return $this->_species;
}
public function get_name()
{
return $this->_name;
}
}
class pterodactyl
extends animal
{
public function __construct()
{
$this->_name = $name;
$this->_species = 'dinosaur';
}
}
$my_big_birdie = new pterodactyl('Daryl');
var_dump($my_big_birdie->get_name()); // string(5) 'Daryl'
var_dump($my_big_birdie->get_species()); // NULL
Calling the get_name
method returns the name we set, since that variable was protected and available in the child class. By setting _species
in the child class, it created a new public property called _species, and left the private _species property in the parent class NULL. A full dump of $my_big_birdie
will show us this:
Object(pterodactyl)#1 (3) {
["_species":"animal":private]=> NULL
["_name":protected]=> string(5) "Daryl"
["_species"]=> string(8) "dinosaur"
}
Ruby doesn’t exactly have properties as we think of them. Instead you have to define getter and setter methods for any instance variables you want to access. These are commonly referred to as readers and writers. (Variables with an @ symbol in front of them are instance variables, which means that they’re available inside the scope of the class.)
class Animal
def initialize(name)
@name = name
end
def name
@name
end
def name=(name)
@name = name
end
def species
@species
end
end
In this example we’ve implemented the animal class from PHP into Ruby. Note how the setter is defined by simply adding an equals to the end of the method name.
Since writing getters and setters can be a repetitive process, Ruby simplifies this by enabling us to declare which instance variables we want available as attributes:
class Animal
attr_reader :species
attr_accessor :name
def initialize(name)
@name = name
end
end
We now have the same class as before, except we’re creating our getters and setter with the Ruby attr_reader
and attr_accessor
. attr_reader
creates only a getter method, attr_writer
(unused here) only a setter method and attr_accessor
creates both methods. 90% of the time this is all we need to do to expose our variables as attributes and for those times where you need more functionality, you can simply use the standard method construct.
Ruby also has a different implementation of Public, Private and Protected. By default all methods are public
, but you can declare visibility by making a single protected
or private
statement and then any methods that follow will have that visibility. This allows you to group together methods with the same visibility:
class MyClass
# the default visibility is public
def my_public_method
end
protected
def my_protected_method
end
def my_other_protected_method
end
private
def my_private_method
end
end
Ruby’s implementation of protected and private is very different to PHP’s and has nothing at all to do with object inheritance. Private in Ruby is more akin to Protected in PHP. A private method in Ruby can be accessed inside the context of an object, or any inherited object.
class Foo
private
def bar
puts "bar called"
end
end
class Blarg < Foo
def initialize
bar
end
end
Because Blarg
class extends Foo
, it’s able to call the private method bar.
The implementation of protected in Ruby is different entirely to anything in PHP. Protected methods can be called by any object of the same class:
class Person
attr_writer :mother
def initialize(name)
@name = name
end
def mother_name
@mother.name if
end
protected
def name
@name
end
end
me = Person.new('Mal')
mum = Person.new('Jeni')
me.mother = mum
puts me.mother_name # => Jeni
puts me.name # => NoMethodError: protected method `name' called for #
Here we have a person class that allows protected access to the name only. When I create two instances and set the mother attribute of one instance to the other instance, then the mother_name
method is capable of calling the mother variable’s name
attribute, however I’m not capable of calling the name
attribute outside the scope of the object itself.
This was a brief overview of some of the fundamental differences between PHP and Ruby’s class properties and method visibility—can you think of any other core differences that are worth mentioning?