Nesneye Yönelimli Php
Classlarla ilgili ingilizce bir makale
- Definition - A look at defining classes and objects.
- Relationships - Making classes and objects work together.
- Taking Relationships Further - Visibility and polymorphism.
- PHP5’s Improved Features - More object-oriented functionality.
So, without further ado, let’s dive straight into Part 1: Definition.
Part 1: Definition
Adopting object-oriented techniques when developing your PHP scripts and applications can immediately inject greater flexibility and easier maintenance into your development environment. Programs become easier to extend, or debug, and sharing code with other developers on your team (should you have one) becomes simpler.
Objects and Classes
Objects in PHP cannot be declared literally; they must be instantiated from a class. This is, in fact, the normal way to create an object - Javascript is the only language I’ve come across where an object can be declared literally. It also makes life simpler here, since I only have to describe the instantiation method.
There’s No Substitute for Class
To define a class in PHP, we write the following code:
{
var $attribute1;
var $attribute2;
function method1()
{
// Code here
return $something;
}
function method2()
{
// Code here
}
}
To instantiate our class (ie. create an instance), we need to use the new
keyword - much like the new
keyword in Javascript:
This creates an object. An object is, in effect, a new variable with a user-defined data-type (the class). This basically means we could instantiate our class over and over into as many different objects as we so desire.
$myObject3 = new myClass();
$myObject4 = new myClass();
Any New Members?
To access the various attributes and methods (members) of our object, we can use the namespace separator ->
(sometimes called the arrow operator; although it’s not really an operator). When you are accessing a variable in PHP, the part of code after the $
is known as the namespace. When you are accessing attributes and methods that are members of an object, you need to extend that namespace to include the name of your object. To do this, you reference attributes and methods like so:
$myObject->attribute1 = 'Sonic';
$myObject->attribute2 = 'Knuckles';
// Methods
$returned = $myObject->method1();
$myObject->method2();
This idea of namespace and namespace separators is very similar to a standard directory structure - the slashes or backslashes becoming the ->
separator. Since we can have objects within objects, we can also extend this further. For example, if our $myObject
object was a member of another object called $anotherObject
, we could reference the members of $myObject
like so:
$anotherObject->myObject->attribute1 = 'Sonic';
$anotherObject->myObject->attribute2 = 'Knuckles';
// Methods
$returned = $anotherObject->myObject->method1();
$anotherObject->myObject->method2();
Notice that the $
appears only once before the namespace; this is because a variable name in PHP can only ever have a single dollar sign. If we were to place another $
anywhere in our namespace, we’d be telling PHP we were using variable variables. I don’t really want to go too far off topic here, so have a look at the PHP documentation for more information about variable variables.
The $this
Pseudo-Variable
When writing code within a class definition, you’re never going to know the name of the object to which it belongs; this is because the object hasn’t even been instantiated at this point. In order to access the attributes and methods of your class from within the class itself, we will need to use the $this
pseudo-variable:
{
var $attribute1;
function method1()
{
// Let's return the value of attribute1
// within this object!
return $this->attribute1;
}
}
$this
can also be thought of as ‘my own’ or ‘current object’. In our example, we’re returning the value of $attribute1
within the ‘current object’.
Constructor (Yapılandırıcı)
The constructor of a class is a special method that is run upon instantiation (when an object is created). It’s main purpose is to set-up the attributes of a class and to establish the class invariant - to basically make sure that the attributes of the class conform to the class interface (a class interface is inferred in PHP4 but can be defined in PHP5; we’ll talk about that in part 5 of this tutorial since, for now at least, it’s unimportant). A properly written constructor should never contain any functionality that could fail, thus leaving the object in an invalid state.
In PHP4, we define our constructor by giving the function the same name as the class itself. Here’s an example:
class database
{
var $str_schema;
var $str_host;
var $str_user;
var $str_password;
// The constructor
function database($str_schema, $str_host, $str_user, $str_password)
{
// Set-up the class variables from the parameters.
$this->str_schema = (string) $str_schema; // It's good practice to use type-casting.
$this->str_host = (string) $str_host;
$this->str_user = (string) $str_user;
$this->str_password = (string) $str_password;
}
}
$db = new database('myschema', 'localhost', 'username', 'password');
//new database komutu ile database klasından türeyen $db içirisindeki
//constructor fonksiyon çalıştırılıyor ve oraya 4 adet değer yollanıyor.
//constructor fonksiyon (database) içerisinde bu değerler class'a aktarılıyor
?>
In this example the constructor receives four parameters. The values of these parameters are then passed to the four class attributes. This is usually the kind of action that is taken by the constructor - it avoids us having to directly set the value of our class attributes which, although possible, would undermine our class interface.
We can now instantiate the class like so - specifying our object’s attributes as we go:
In PHP5, however, we declare our constructor using the new __construct
function:
{
var $str_schema;
var $str_host;
var $str_user;
var $str_password;
// The constructor
function __construct($str_schema, $str_host, $str_user, $str_password)
{
// Set-up the class variables from the parameters.
$this->str_schema = (string) $str_schema; // It's good practice to use type-casting.
$this->str_host = (string) $str_host;
$this->str_user = (string) $str_user;
$this->str_password = (string) $str_password;
}
}
It’s worth noting that the method used to declare a constructor in PHP4 (ie. naming it the same as its class) will still function as expected in PHP5. This is to allow for backwards compatibility.
Destructor (Yapılandırıcı Yokedici)
A destructor function is run when an object is destroyed - its role is to clean up and to free the resources which were used by the object during run-time, and to unlink the object from other objects or resources.
In PHP4 this functionality is sadly unavailable, although it’s perfectly possible to write a specific destructor method within your class and run it before you destroy the object. You can take this idea a step further by registering your method to run when script processing is complete using the register_shutdown_function
function like so:
register_shutdown_function(yourFunctionHere);
For more information on this, you should read the register_shutdown_function
entry in the PHP documentation.
PHP5 allows us to specify an object destructor using the __destruct
function. Here’s an extention of our constructor example that assumes we might have created a MySQL connection:
class database
{
var $str_schema;
var $str_host;
var $str_user;
var $str_password;
var $res_connection;
// The constructor
function __construct($str_schema, $str_host, $str_user, $str_password)
{
// Set-up the class variables from the parameters.
$this->str_schema = (string) $str_schema; // It's good practice to use type-casting.
$this->str_host = (string) $str_host;
$this->str_user = (string) $str_user;
$this->str_password = (string) $str_password;
//$this->VeriTabaninaBaglan();
}
function VeriTabaninaBaglan(){
$this->res_connection=mysql_connect($this->str_host, $this->str_user, $this->str_password);
if(!$this->res_connection){
echo "Veri Tabanına Bağlanamadı";
}
}
// The destructor
function __destruct()
{
// Close a mysql connection we might've created earlier
if ($this->res_connection)
{
if(mysql_close($this->res_connection)){
echo "Bağlantı Koparılmıştır";
}
}
}
}
$YeniKlass= new database("Baglanti_1", "localhost", "root", "1");
$YeniKlass->VeriTabaninaBaglan();
$YeniKlass=NULL; // __destrucut() fonksiyonunu çağırdı
?>
Because PHP is a scripting language and is therefore stateless, the role of the destructor is often negated by the ending of script execution. However the destructor is still useful should this not be the case. It can be argued, however, that you should always include a destructor simply so that you can always be assured of clean object destruction.
Summary
You should be fairly confident in creating classes and objects now; allowing increased modularity and encapsulation within your PHP scripts. As I stated earlier in my “Object-Oriented Concepts” post, OOP provides greater flexibility and easier maintainance across large systems and can sometimes make understanding and analysing complex procedures a lot easier.
In the next post, I’ll be taking this functionality to the next level by introducing object-oriented relationships into our PHP.
RelationshipsThe relationships you use dictate the way objects interact with each other. Here’s how to model them in PHP.
Inheritance (Miras)
Inheritance allows us to define a class that inherits all the attributes and methods of another class. To do this in PHP4 and PHP5, we need to employ the extends
keyword. Here’s an example based around HTML form elements:
{
var $str_id;
var $str_name;
var $str_class;
function isRequired()
{
// Validation code here
}
}
class dateInput extends formElement
{
var $str_value;
// Specific validation for dateInput
function isValidDate()
{
// Validation code here
}
}
In the above example, the class dateInput
is inheriting all the attributes and methods of the class formElement
. This means we could do all of the following:
// Instantiate our class as an object
$myDate = new dateInput(); // dateInput() klasından myDate Klası üretiliyor
$myDate->str_id = 'mydate'; //str_id formElement classına ait değişken
$myDate->str_name = 'mydate'; //str_id formElement classına ait değişken
$myDate->str_value = '2006/06/09'; //str_value dateInput classına ait değişken
$myDate->isRequired(); //str_id formElement classına ait değişken
$myDate->isValidDate(); //str_id dateInput classına ait değişken
Some languages allow multiple inheritance; allowing you to inherit from more than one class. Unfortunately, this is not the case in PHP.
If your parent class has a constructor, PHP won’t evaluate it when you instantiate the child class. If you wish the constructor to be run, you’ll need to call in as if it were any other method within the class. Here’s an example of what I mean:
PHP4
{
// Constructor
function fruit()
{
}
}
class apple extends fruit
{
function apple()
{
// Call the constructor of our parent class
$this->fruit();
}
}
{
// Constructor
function __construct()
{
}
}
class apple extends fruit
{
function apple()
{
// Call the constructor of our parent class
// using the scope resolution operator -
// we'll talk about this later.
parent::__construct();
}
}
In the PHP5 example you’ll notice we don’t use the $this
keyword. This is simply because $this
could evaluate to either apple
or fruit
because the constructor method for each is called __construct
. To get around this we use the scope resolution operator which I will talk about a little later on.
Composition - Association and Aggregation
Association and aggregation differ from inheritance because rather than passing the methods and properties of a class to another class, they involve instantiating whole objects and using them as properties, or passed-in parameters, in another class.
This might sound complicated but it’s really very simple in practice. I’m going to borrow some of the examples in my Object-Oriented Concepts post and adapt them for PHP:
Association
{
var $sample_attribute;
}
class wall
{
var $brick1;
var $brick2;
var $brick3;
var $brick4;
// Constructor
function wall()
{
$this->brick1 = new brick();
$this->brick2 = new brick();
$this->brick3 = new brick();
$this->brick4 = new brick();
}
}
In the above example we have a class wall
which has four brick
attributes. To associate our brick
class we instantiate it as the $brick
attributes of the wall
class.
We can now access the $sample_attribute
of each brick within our wall
class like so:
echo $this->brick2->sample_attribute;
echo $this->brick3->sample_attribute;
echo $this->brick4->sample_attribute;
Or from outside an instantiated object like so:
echo $myWall->brick1->sample_attribute;
echo $myWall->brick2->sample_attribute;
echo $myWall->brick3->sample_attribute;
echo $myWall->brick4->sample_attribute;
As you can see, this type of relationship has the potential to be highly useful - however, if you wish to share an object between other classes (for instance, a database connection object) we run into a problem. In our example you can see that the brick
objects are encapsulated within our wall
class which means they are only accessible within our myWall
object. To get around this problem we could use aggregation.
Aggregation basically involves passing an instantiated object into a class so that the class can “use” our object. Once again, I’ll poach one of my earlier examples to illustrate what I mean. To save on code snippets, I’m only going to use PHP4 syntax for the moment:
Aggregation
{
}
class car
{
var $driver;
// Constructor
function car(&$driver)
{
$this->driver = (object) $driver;
}
}
// Instantiate our person object
$me = new person();
// Now pass it in as $driver
$myMotor = new car($me);
In this example we instantiate our class person
as an object ($me
). We then pass the $me
object into the car class so that it is used as the $driver
attribute in the $myMotor
object. If we were to destroy the $myMotor
object, we wouldn’t destroy the $me
object; which means we could carry on using it for other things. In fact, even though we’re using the $me
object within our $myMotor
object, we could still use it in other objects like so:
class van
{
var $driver;
// Constructor
function van(&$driver)
{
$this->driver = (object) $driver;
}
}
// Our $me object already exists
// so pass it in as $driver
$myVan = new van($me);
If you look at our example you’ll notice the ampersand symbol (&) before the parameter declaration of our constructor method. This tells PHP that the parameter is passed by reference. This is the key to the aggregation relationship.
References
PHP references allow you to define two variables that refer to the same content. To do this we use the &
operator. Here’s an example:
$a =& $b;
In this example $a
and $b
point to the same content.
The same syntax can be used with functions, that return references, and with the new
operator:
$bar =& new fooclass();
$foo =& find_var($bar);
If we don’t use the &
operator when we’re instantiating an object, PHP creates a copy of said object. If you use $this
in the class it will operate on the current instance of the class. The assignment without &
will copy the instance (i.e. the object) and $this
will operate on the copy, which is not always what is desired. Usually you want to have a single instance to work with, due to performance and memory consumption issues.
Basically, it’s a good idea to always assign a reference when instantiating an object unless you have a specific case not to.
Note: Since PHP5, in fact, new
returns references automatically so using =&
in this context is deprecated and produces E_STRICT level messages.
There is another way that references help us when developing object-oriented solutions in PHP; by allowing method parameters to be passed by reference as we did in our aggregation example above.
Passing by reference basically allows us to use or modify a variable within a method (function). Here’s a good example from the PHP documentation:
function foo(&$var)
{
$var++;
}
$a=5;
foo($a);
// $a is 6 here
In the aggregation example we’re passing an entire object as a parameter. If we passed the parameter without the reference we would be creating a copy of the object that was only available within the function. By adding the reference we’re simply telling the function that the parameter is another name for our original object.
Summary
Although these relationships can appear simple to begin with, it’s all too easy to overlook their hidden power. Used in the right way, relationships can help to build highly powerful, easily extendable applications with relatively little fuss.
Deciding which relationship to use can be tricky; and it’s certainly not something you’ll get right everytime. However, rest assured, if you experiment and use them regularly, you’ll find it easier to solve problems that are presented to you.
In the next post, I’ll be taking a look at extending our relationships a little further using visibility and polymorphism.
Access Modifiers (aka Visibility)
Access modifiers allow you to alter the visibility of any class member. They have long been an important part of OOP and have finally been implemented in PHP as of version 5 - in fact they replace the deprecated var
declaration. However, if you’re still a member of the PHP4 posse, don’t fret; there’s a little known suggestion in the PHP manual that can help us to imply private
. I’ll cover that in a second.
To define the visibility of a class member we need to prefix it with one of the keywords public
, private
or protected
. Public declared members can be accessed everywhere. Protected limits access to inherited and parent classes (and to the class that defines the item). Private limits visibility only to the class that defines the item.
To illustrate this in action, I’m going to poach a couple of examples directly from the PHP manual. Firstly, let’s take a look at access modifiers applied to class attributes:
class myClass
{
public $public = 'Public';
protected $protected = 'Protected';
private $private = 'Private';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj = new myClass();
echo $obj->public; // Works
echo $obj->protected; // Fatal Error
echo $obj->private; // Fatal Error
// Shows Public, Protected and Private
$obj->printHello();
class myClass2 extends myClass
{
// We can redeclare the public and
// protected method, but not private
protected $protected = 'Protected2';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj2 = new myClass2();
echo $obj->public; // Works
echo $obj2->private; // Undefined
echo $obj2->protected; // Fatal Error
// Shows Public, Protected2, not Private
$obj2->printHello();
Now let’s take a look at access modifiers applied to methods:
class myClass
{
// Contructors must be public
public function __construct() { }
// Declare a public method
public function myPublic() { }
// Declare a protected method
protected function myProtected() { }
// Declare a private method
private function myPrivate() { }
// This is public
function foo()
{
$this->myPublic();
$this->myProtected();
$this->myPrivate();
}
}
$myclass = new myClass;
$myclass->myPublic(); // Works
$myclass->myProtected(); // Fatal Error
$myclass->myPrivate(); // Fatal Error
// Public, Protected and Private work
$myclass->foo();
class myClass2 extends myClass
{
// This is public
function foo2()
{
$this->myPublic();
$this->myProtected();
$this->myPrivate(); // Fatal Error
}
}
$myclass2 = new myClass2;
$myclass2->myPublic(); // Works
// Public and Protected work, not Private
$myclass2->foo2();
For users of PHP4 there are, unfortunately, no access modifiers available. However, under “Object Aggregation/Composition Functions” in the PHP manual the following is noted:
Note that for the purposes of object aggregation, private elements of a class/object begin with an underscore character (”_”), even though there is not real distinction between public and private class/object elements in PHP.
With this in mind, I usually declare private members with an underscore character regardless of whether it’s for aggregation or not.
Polymorphism
An object-oriented programming language must support polymorphism; meaning different classes can have different behaviours for the same attribute or method. This can best be illustrated with the same example I used in my “Object-Oriented Concepts” post:
class formElement
{
var $id;
var $name;
var $class;
function getHtml()
{
// returns generic form element HTML
// using heredoc to save on escapes
return <<<EOD
<input id="{$this->id}" name="{$this->name}" class="{$this->class}"/>
EOD;
}
}
class textarea extends formElement
{
var $cols;
var $rows;
function getHtml()
{
// returns generic form element HTML
// using heredoc to save on escapes
return <<<EOD
<textarea id="{$this->id}" name="{$this->name}" class="{$this->class}" cols="{$this->cols}" rows="{$this->rows}"></textarea>
EOD;
}
}
As you can see in the example, both classes have the method “getHtml” but the “textarea” class is a subclass of the “formElement” class. This results in the “getHtml” method being overloaded. Overloading is a good example of polymorphism in action - an object-oriented language must support polymorphism in order to know which “getHtml” method applies to which object.
Polymorphism, at its most basic, describes the fact that a given function may have different specifications, depending on the object to which it is applied.
Scope Resolution Operator
The Scope Resolution Operator, “::” (double-colon or Paamayim Nekudotayim - that’s double-colon in Hebrew - don’t ask me; ask the Zend team!), allows us to access a method or an attribute from an uninstantiated class. This is incredibally useful if you wish to create a class of functions but don’t want to instantiate an object of that class. Here’s an example class:
class validation
{
function isValidEmail($str_data)
{
return (!eregi("^[_a-z0-9-]+(.[_a-z0-9-]+)*@[a-z0-9-]+(.[a-z0-9-]+)*(.[a-z]{2,3})$", $Email)) ? false : true;
}
function isLessThan($str_data, $str_compare)
{
return ($str_data < $str_compare) ? true : false;
}
function isGreaterThan($str_data, $str_compare)
{
return ($str_data > $str_compare) ? true : false;
}
}
Now if we want to check an email address, we can access the isValidEmail
method, without instantiating the class, like so:
$bln_valid = validation::isValidEmail('big.cheese@dairy.com');
The PHP documentation takes this one step further by illustrating how the scope resolution operator can benefit polymorphism:
class A {
function example() {
echo "I am the original function A::example().n”;
}
}
class B extends A {
function example() {
echo “I am the redefined function B::example().n”;
A::example();
}
}
// there is no object of class A.
// this will print
// I am the original function A::example().
A::example();
// create an object of class B.
$b = new B;
// this will print
// I am the redefined function B::example().
// I am the original function A::example().
$b->example();
In this example you can see that class B
overloads the example()
method. Since this is the case, the original example()
method is “shadowed” and is therefore no longer available - that is, however, unless we use the scope resolution operator to access it. This basically allows us to extend the functionality of an overloaded method in a child class. Isn’t that just incredibally useful?
parent
PHP takes this particular use of the scope resolution operator one step further by allowing us to reference our parent class without having to use its literal name. To do this, we use the parent
special name like so:
class A {
function example() {
echo "I am A::example() and provide basic functionality.n”;
}
}
class B extends A {
function example() {
echo “I am B::example() and provide additional functionality.n”;
parent::example();
}
}
$b = new B;
// This will call B::example(), which will in turn call A::example().
$b->example();
By using this method you are avoiding using the name of your parent class in too many places - thus you will only need to change the class name in the extends clause.
PHP5 and the Scope Resolution Operator
In PHP5 any methods or attributes you want to access with the scope resolution operator must be declared as static. The static
declaration must appear after the visibility declaration. However, if no visibility declaration is included (PHP4 compatibility), the member will be treated as public:
class monkey
{
public static $int_bananas = 6;
public static function addBananas($int_bananas)
{
// Add the new bananas to our
// int_bananas attribute. I'll explain the self
// keyword a little further below...
self::$int_bananas += (int) $int_bananas;
}
}
Any attributes you declare as static are subsequently unavailable within instantiated objects. Static methods, however, are available but, since they are callable without an object instance being created, cannot use the $this
pseudo variable.
To reference a static member from within the same class, we can use the self
special name; as seen in the previous example.
Static method calls are resolved at compile time. This means that, when we’re using an explicit class name, the method is already identified completely and, subsequently, no inheritance rules apply. This is also the case when using the self
special name.
Static properties cannot be accessed through the object using the ->
namespace separator.
Calling non-static methods statically generates an E_STRICT level warning.
Summary
As you can see from the above examples, polymorphism and visibility extend the relationships between your objects and classes ten-fold. In fact, you’ll find them invaluable once you start taking them into account during the code planning phase of your next big project. The scope resolution operator will allow you to take polymorphism once step further or to simplify relationships that you may be over-complicating (I’ve done that more than once, believe me).
Well that’s pretty much it for all you PHP4 developers out there - unfortunately the complex functionality I’m going to look at in the next post is limited to the updated object model in PHP5. However, I’d recommend you read it simply to improve your understanding of object-oriented systems. Of course, it’ll also help you make the decision to upgrade (or to keep pestering your boss to upgrade).
Class Constants
Most PHP developers will be sufficiently acquainted with standard constants:
define('ELVIS', 'The King!');
echo ELVIS; // Outputs "The King!"
In PHP5 we are able to define class specific constants that behave much like static
members; ie. we can’t access them from an instantiated object.
To define a class constant, we write the following code:
class englandSquad
{
const goalie = 'Robbo';
function display()
{
echo self::goalie;
}
}
In the above example our display()
method will output our goalie
constant. We could also echo this constant like so:
echo englandSquad::goalie;
However, the following code would not work because we’re trying to use an instantiated object:
$team = new englandSquad();
echo $team::goalie;
echo $team->goalie;
A constant must always be just that; a constant expression. It cannot be a variable, the result of a function call or a class method.
Abstract Classes
Abstract classes contain abstract methods - a special kind of method that simply allows a developer to define a template for methods that should be included in the child classes of our abstract class. Although we can define the method’s signature, we cannot define the implementation.
The child classes of our abstract class (ie. those that inherit from it) must contain a definition of the abstract methods within that class. Those methods should also be defined with the same (or weaker) visibility.
To better illustrate what I mean, I’m going to directly lift the example from the PHP manual since I feel it performs a perfect explanation of this particular feature:
abstract class AbstractClass
{
// Force Extending class to define this method
abstract protected function getValue();
abstract protected function prefixValue($prefix);
// Common method
public function printOut() {
print $this->getValue() . "n";
}
}
class ConcreteClass1 extends AbstractClass
{
protected function getValue() {
return "ConcreteClass1";
}
public function prefixValue($prefix) {
return "{$prefix}ConcreteClass1";
}
}
class ConcreteClass2 extends AbstractClass
{
public function getValue() {
return "ConcreteClass2";
}
public function prefixValue($prefix) {
return "{$prefix}ConcreteClass2";
}
}
$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."n";
$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') ."n";
This example will output the following:
ConcreteClass1
FOO_ConcreteClass1
ConcreteClass2
FOO_ConcreteClass2
As you can see from the example, our abstract class can still contain other members that we wish the child classes to inherit - even though they’re not abstract themselves.
Object Interfaces
Taking this idea of “templates” one step further, we are able to specify object interfaces.
In much the same way that we can define method templates (abstract methods) in our abstract classes, interfaces allow us to specify which methods a class must implement. We do not have to specify how those methods are handled, however.
We specify an interface like so:
interface iObserver
{
public function update();
}
Notice that we haven’t defined the contents of the method update()
. Also, notice that the method is declared as public
. All methods declared in an interface must be public as this is the very nature of an interface.
We can now apply our interface to a class using the implements
operator. Once we have done this, all the methods within our interface must be included in our class definition:
class StatisticsDisplay implements iObserver
{
public function update()
{
// As an observer, StatisticsDisplay
// checks for updates.
// Because we're using an interface,
// this code is still class specific!
}
public function display()
{
// Display our statistics
}
}
class ForecastDisplay implements iObserver
{
public function update()
{
// As an observer, StatisticsDisplay
// checks for updates.
// Because we're using an interface,
// this code is still class specific!
}
public function display()
{
// Display our forecast
}
}
As you can see, we’ve included the update()
method in both our classes since we’re implementing our iObserver
interface.
Incidentally, anyone who understands Design Patterns might already have spotted that I’m using the Observer Pattern in this particular example.
We could create another interface for our display()
methods (since they seem to be common) and also apply it to our classes like so:
interface iDisplay
{
public function display();
}
// Notice the comma in the following line of code
class StatisticsDisplay implements iObserver, iDisplay
{
public function update()
{
// As an observer, StatisticsDisplay
// checks for updates.
// Because we're using an interface,
// this code is still class specific!
}
public function display()
{
// Display our statistics
}
}
class ForecastDisplay implements iObserver
{
public function update()
{
// As an observer, StatisticsDisplay
// checks for updates.
// Because we're using an interface,
// this code is still class specific!
}
public function display()
{
// Display our forecast
}
}
It’s worth remembering, however, that a class cannot implement two interfaces that share function names, since it would cause ambiguity.
Magic Methods
There are a number of “magic” methods included in PHP5 that allow a developer to extend the functionality of classes at runtime. I’m going to look at a few of these here but for more information on all these methods, please take a look at Magic Methods in the PHP manual.
Also, before I get started, thanks go out to Brendon Kozlowski for requesting this information since I hadn’t intended on including it until he raised the point!
Member Overloading
The magic methods __set()
, __get()
, __isset()
and __unset()
allow a developer to overload the specific behaviour of that method for a given class.
For example, the __get()
method allows you to overload behaviour when getting class members:
class MyClass
{
public $member;
private function __get($name)
{
echo "Getting {$name}...n";
}
}
$obj = new MyClass();
$obj->member = 1;
echo $obj->member;
The above code will output the following:
Getting member...
1
The __set()
method is slightly different because it expects two parameters - one for member name and one for the value being passed to it:
class MyClass
{
public $member;
private function __set($name, $value)
{
echo "Setting {$name} to {$value}...n";
}
}
$obj = new MyClass();
$obj->member = 1;
echo $obj->member;
The above code will now output the following:
Setting member to 1...
1
Method Overloading
We can also overload our calls to class methods by using the __call()
method in much the same way.
The __call()
method takes two parameters - the name of the function called and an array of the arguments that were passed.
To save time thinking up examples, here’s one from the PHP manual:
class Caller
{
private $x = array(1, 2, 3);
private function __call($m, $a)
{
print "Method $m called:n";
var_dump($a);
return $this->x;
}
}
$foo = new Caller();
$a = $foo->test(1, "2", 3.4, true);
var_dump($a);
This example will output the following:
Method test called:
array(4) {
[0]=>
int(1)
[1]=>
string(1) "2"
[2]=>
float(3.4)
[3]=>
bool(true)
}
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
__toString
Another useful magic method is the __toString()
method. This basically allows us to overload the behaviour performed when converting an object to a string. A nice simple example of this would be:
class TestClass
{
public $foo;
public function __construct($foo) {
$this->foo = $foo;
}
public function __toString() {
return $this->foo;
}
}
$class = new TestClass('Hello');
echo $class;
Which would output the following:
Hello
Summary
Well, we’ve reached the end of our journey through the wonders of object-oriented PHP and I feel I should clarify that I’ve really only scratched the surface of the object-oriented power of the language. I’m sure I’ve missed all kinds of functionality that experienced developers will be sending me snooty emails about for years to come - all I can say is, for more information, please read the manual!
Hopefully now you’ll have more than a little understanding in the general techniques used in an object-oriented system and, although you might not be 100% confident that you understand the methodology, with use you’ll be an object-oriented developer in no time. So what are you waiting for? Go out and start using them!
Kaynak nefariousdesigns.co.uk/archive/2006/08/object-oriented-php-part-1-definition/