An Introduction to Object-Oriented Programming

Introduction

Object-oriented (OO) software development can be a confusing topic for developers who create primarily procedural code. But it doesn't need to be. In this chapter we explore some of the basic theory behind OO and cover the (sometimes daunting) multisyllabic terminology. You'll learn why you should be interested in OO techniques, how they can really improve the speed with which you develop complex applications, and the ease with which you can modify those applications.

What is Object-Oriented Programming?

Object-oriented programming (OOP) is a different way of thinking about the way you construct your applications. Objects enable you to more closely model the real world things, processes, and ideas that your application is designed to handle. Instead of thinking about an application as a thread of control that passes chunks of data from one function to the next, an OOP approach allows you to model the application as a set of collaborating objects that independently handle certain activities.

For example, when a house is being constructed, the plumbers deal with the pipes, and the electricians deal with the wires. The plumbers don't need to know whether the circuit in the bedroom is 10 amps or 20. They need only concern themselves with their own activities. A general contractor ensures that each subcontractor is completing the work that needs to be accomplished but isn't necessarily interested in the particulars of each task. An OO approach is similar in that each object hides from the others the details of its implementation. How it does its job is irrelevant to the other components of the system. All that matters is the service that the object is able to provide.

The concepts of classes and objects, and the ways in which you can leverage these ideas in the development of software, are the fundamental ideas behind object-oriented programming. This is, in a sense, the opposite of procedural programming, which is programming using functions and global data structures. As you'll see, an object-oriented approach gives you some big benefits over procedural programming, and with the new implementation of OO support in PHP5, some large performance boosts, as well.

Let's take a look at some of the enormous advantages that an OOP approach to software development gives you.
First is the ease with which you can map business requirements to code modules. Because an OOP approach enables you to model your application based on the idea of real world objects, you can often create a direct mapping of people, things, and concepts to PHP classes. These classes have the same properties and behaviors as the real-world concepts they represent, which helps you to quickly identify what code needs to be written and how different parts of the application need to interact.

A second benefit of OOP is code reuse. You frequently need the same types of data in different places in the same application. For example, an application that enables a hospital to manage its patient records would definitely need a class called Person. There are a number of people involved in patient care—the patient, the doctors, the nurses, hospital administrators, insurance claims people, and so on. At each step in the care of the patient, that patient's record requires a note about which person was performing a given action (such as prescribing medicine, cleaning wounds, or sending a bill to an insurance carrier) and verifying that person is allowed to perform that action. By defining a class called Person that encompasses all the properties and methods common to all of these people, you get an enormous amount of code reuse that isn't always possible in a procedural programming approach.

What about other applications? How many applications can you think of that at some point handle information about individuals? Probably quite a few. A well-written Person class could easily be copied from one project to another with little or no change, instantly giving you all the rich functionality for dealing with information about people that you developed previously. This is one of the biggest benefits of an OO approach—the opportunities for code reuse within a given application as well as across different projects.

Another OOP advantage comes from the modularity of classes. If you discover a bug in your Person class or you want to add to or change the way that class functions, you have only one place to go. All the functionality of that class is contained in a single PHP file. Any processes of the application that rely on the Person class are immediately affected by changes to it. This can vastly simplify the search for bugs and makes the addition of features a relatively painless task.
It might seem trivial in a smaller application, but in a more complex software architecture the benefits of modularity can be enormous. I once worked on a project involving more than 200,000 lines of procedural PHP code. Easily 65 percent of the time spent fixing bugs was devoted to uncovering where certain functions were located and determining which data interacted with which functions. A rewrite of that software in an OO architecture resulted in dramatically less code, which would have meant not only less work (if it had been done that way in the first place) but also fewer bugs (the less code there is, the fewer the opportunities for problems) and a faster turnaround time on bug fixes.
Because an OO approach forces you to think about how the code is organized, it's a lot easier to discover the structure of an existing application when you are new to the development team; you have a framework to guide the location of new functions you develop.

On larger projects, there is often a multimember software development team, usually composed of programmers with varying skill levels. Here, too, an OO approach has significant benefits over procedural code. Objects hide the details of their implementation from the users of those objects. Instead of needing to understand complex data structures and all of the quirks of the business logic, junior members of the team can, with just a little documentation, begin using objects created by senior members of the team. The objects themselves are responsible for triggering changes to data or the state of the system.

When the large application I mentioned previously was still using procedural code, it could often take up to two months for new members of the software development team to learn enough about the application to be productive. Once the software was rebuilt using objects, it usually took no more than a couple of days for new members of the team to begin making substantial additions to the code base. They were able to use even the most complex objects quickly because they did not need to fully understand the particulars of how the functionality contained within those objects was implemented.

Now you have a good idea about why you should consider using an OO paradigm as your programming method of choice. The following sections offer you a better understanding of the fundamental concepts behind OO.

Understanding OOP Concepts

This section introduces the primary concepts of object-oriented programming and explores how they interact.

• Classes, which are the "blueprints" for an object and are the actual code that defines the properties and methods.
• Objects, which are running instances of a class and contain all the internal data and state information needed for your application to function.
• Inheritance, which is the ability to define a class of one kind as being a sub-type of a different kind of class (much the same way a square is a kind of rectangle).
• Interfaces, which are contracts between unrelated objects to perform a common function.
• Encapsulation, which is the capability of an object to protect access to its internal data.

Along the way, we'll discuss polymorphism, which allows a class to be defined as being a member of more than one category of classes (just like a car is "a thing with an engine" and "a thing with wheels").

Classes

In the real world, objects have characteristics and behaviors. A car has a color, a weight, a manufacturer, and a gas tank of a certain volume. Those are its characteristics. A car can accelerate, stop, signal for a turn, and sound the horn. Those are its behaviors. Those characteristics and behaviors are common to all cars. Although different cars may have different colors, all cars have a color. OOP enables you to establish the idea of a car as being something with all of those characteristics through the use of a construct known as a class. A class is a unit of code, composed of variables and functions, which describes the characteristics and behaviors of all the members of a set. A class called Car, for example, would describe the properties and methods common to all cars.

In OO terminology, the characteristics of a class are known as its properties. Properties have a name and a value. Some allow that value to be changed while others do not. For example, the Car class would probably have such properties as color and weight. Although the color of the car can be changed by giving it a new paint job, the weight of the car (without cargo or passengers) is a fixed value.

Some properties represent the state of the object. State refers to those characteristics that change due to certain events but are not necessarily directly modifiable on their own. In an application that simulates vehicle performance, the Car class might have a property called velocity. The velocity of the car is not a value that can be changed on its own, but rather is a byproduct of the amount of fuel being sent to the engine, the performance characteristics of that engine, and the terrain over which the car is traveling.

The behaviors of a class—that is, the actions associated with the class—are known as its methods. Methods are implemented as functions. Like functions, methods can accept parameters of any valid data type. Some methods act on external data passed to them as parameters but they can also act on the properties of their object either using those properties to inform actions made by the method, such as when the accelerate method examines the remaining amount of fuel to determine whether the car is capable of accelerating, or to change the state of the object by modifying values such as the velocity of a car.

Objects
To begin with, you can think of a class as a blueprint for constructing an object. In much the same way that many houses can be built from the same blueprint, you can build multiple instances of an object from its class. But the blueprint doesn't specify things like the color of the walls, or type of flooring. It merely specifies that those things will exist. Classes work much the same way. The class specifies the behaviors and characteristics the object will have, but not necessarily the values of those characteristics. An object is a concrete entity constructed using the blueprint provided by a class. So although the idea of a house is analogous to a class, your house (a specific instance of the idea of a house) is analogous to an object.

With a blueprint in hand and some building materials, you can construct a house. In OOP, you use the class to build an object, a process known as instantiation. Instantiating an object requires two things:

• A memory location into which to load the object. This is automatically handled for you by PHP.
• The data that will populate the values of the properties. This data could come from a database, a flat text file, another object, or some other source.

A class never has property values or state. Only objects can. You have to use the blueprint to build the house before you can give it wallpaper or vinyl siding. Similarly, you have to instantiate an object from the class before you can interact with its properties or invoke its methods. When to use the word class and when to use the word object is often confusing to those new to OOP. Just remember that classes are manipulated at design-time when you make changes to the methods or properties, and objects are manipulated at runtime when values are assigned to their properties and their methods are invoked.
Once an object is instantiated, it can be put to work implementing the business requirements of the application. Let's look at exactly how to do that in PHP.

Creating a Class

Let's start with a simple example. Save the following in a file called class.Demo.php:
<?php

  class Demo {

  }

?>

And there you have it—the Demo class. Not terribly exciting just yet, but this is the basic syntax for declaring a new class in PHP. Use the keyword class to let PHP know you're about to define a new class. Follow that with the name of the class and braces to indicate the start and end of the code for that class.
You can instantiate an object of type Demo like this:
<?php

  require,_once ('class .Demo.php');

  $objDemo = new Demo();

?>

To instantiate an object, first make sure PHP knows where to find the class declaration by including the file containing your class (class.Demo.php in this example). Then invoke the new operator, and supply the name of the class and opening and closing parentheses. The return value of this statement is assigned to a new variable, $objDemo in this example. Now you can invoke the $objDemo object's methods and examine or set the value of its properties—if it actually has any.

It's important to have a clearly defined convention for organizing your source code files. A good rule to follow is to put each class into its own file and to name that file class.[ClassName].php.

Even though the class you've created doesn't do much of anything yet, it's still a valid class definition.

Adding a Method

The Demo class isn't particularly useful if it isn't able to do anything, so let's look at how you can create a method. Remember, a method of a class is basically just a function.
By adding a function inside the braces of your class, you're adding a method to that class. Here's an example:
<?php

  class Demo {

    function sayHello($name) {
      print "Hello $name!";
    }

  }

?>

An object derived from your class is now capable of printing out a greeting to anyone who invokes the sayHello method. To invoke the method on your $objDemo object, you need to use the operator -> to access the newly created function. Save the following code in a file called testdemo.php:
<?php

  require_once('class.Demo.php');

  $objDemo = new Demo();
  $objDemo->sayHello('Steve');

?>

The object is now capable of printing a friendly greeting! The -> operator is used to access all methods and properties of your objects.

For those who have had exposure to OOP in other programming languages, please note that the -> operator is always used to access the methods and properties of an object. PHP does not use the dot operator (.) in its OO syntax at all.
Save the file and then open it in your Web browser. The strings "Hello Steve!" and "Hello Ed!" print to the screen.
The keyword public is used to let the class know that you want to have access to the following variable from outside the class. Some member variables of the class exist only for use by the class itself and should not be accessible to external code. In this example, you want to be able to set and retrieve the value of the property name. Note that the way the say Hello method works has changed. It now fetches the name value from the property instead of taking a parameter.

You use the variable $this so that an object can get information about itself. You might have multiple objects of a class, for instance, and because you don't know in advance what the name of an object variable will be, the $this variable enables you to refer to the current instance.

In the example, the first call to say Hello prints Steve and the second call prints Ed because the $this variable enables each object to talk to itself without having to know the name of the object variable currently referencing it. Remember that some properties influence the action of certain methods, such as when the accelerate method of the Car class, for example, needs to examine the amount of fuel remaining. The code inside accelerate would use code such as $this-
>amountOfFuel to access that property.

When accessing properties, you only need one $. The syntax is $obj->property, not $obj->$property. This often causes confusion for those new to PHP. The property variable is declared as public $property; and accessed using $obj-
>property.

In addition to the variables that store the values for the properties of the class, there may be other variables declared for use by the internal operations of the class. Both kinds of data are collectively referred to as the class's internal member variables. Some of these are accessible to code outside the class in the form of properties. Others are not accessible and are strictly for internal housekeeping.

For example, if the Car class needed to get information from a database for whatever reason, it might keep a database connection handle in an internal member variable. This database connection handle is obviously not a property of the car, but it is something the class needs to carry out certain operations.

Protecting Access to Member Variables

There are three different levels of visibility that a member variable or method can have: public, private, and protected. Public members are accessible to any and all code. Private members are only accessible to the class itself. These are typically items used for internal housekeeping, such as the database connection handle in the Car class that I mentioned earlier. Protected members are available to the class itself, and to classes that inherit from it.

Public is the default visibility level for any member variables or functions that do not explicitly set one, but it is good practice to always explicitly state the visibility of all the members of the class.

By creating get and set functions for all your properties it becomes much easier to add data validation, new business logic or other changes to your objects in the future. Even if the current business requirements for your application involve no data validation of a given property, you should still implement that property with get and set functions so you can add validation or business logic functionality in the future.

As the preceding example shows, you can set the value of the name property to just about anything you want—including an object, an array of integers, a file handle, or any other nonsensical value. However, you don't get an opportunity to do any sort of data validation or update any other values when the name property is set.

Initializing Objects

For many of the classes you create, you'll need to do some special set up when an object of that class is first instantiated. You might need to fetch some information from a database, or initialize some property values, for example. By creating a special function called __construct(), you can perform any activities required to instantiate the object. PHP automatically calls this special function when instantiating the object.
For example, you could rewrite PropertyObject class in the following way:

<?php

class PropertyObject {

   private $_properties;

   public function__construct() {
      $this->_properties = array();
      $this->_properties['name'] = null;
      $this->_properties['dateofbirth'] = null;
}

. . .//remaining code omitted for brevity

The __construct function is invoked automatically when you instantiate a new object of class PropertyObject.

For PHP4 users: In PHP4, object constructors were functions with the same name as the class. PHP5 changed this to use a unified constructor scheme. For backward compatibility, PHP first looks for a function called __construct, but if none is found, it still looks for a function with the same name as the class.

You can also pass parameters to the constructor. These parameters could be used to initialize the values of the properties with data, or to fetch some information from a database. The following example shows the use of the PostgreSQL functions to get some information about a user from a database. This class reuses the same __get and __set methods from the PropertyObject class.

Destroying Objects

The object variables that you create are removed from system memory when the requested page has completed running, when the variable falls out of scope, or when it is explicitly set to null. In PHP5 you can trap the destruction of the object and take actions when that happens. To do so, create a function called __destruct with no parameters. Before the object is destroyed, this function, if it exists, is called automatically.

This gives you the opportunity to perform any last minute clean up, such as closing file handles or database connections that might have been opened by the class, or any other last-minutes housekeeping that might need to be done before the object is destroyed.

Inheritance

If you were creating an application to handle inventory at a car dealership, you'd probably need classes such as Sedan, PickupTruck, and MiniVan that would correspond to the same types of automobiles in the dealer's inventory. Your application would need to show not only how many of these items were in stock, but also report on the characteristics of these vehicles so the salespeople could ably give the information to customers.

A sedan is a four-door car and you'd want to record the back seat space and the trunk capacity. A pickup truck doesn't have a trunk, but it has a cargo bed with a certain capacity, and a towing capacity. A minivan has a number of sliding doors (either one or two) and a number of seats inside.

But each of these vehicles is really just a different type of automobile and as such would share a number of characteristics in your application, such as color, manufacturer, model, year, vehicle identification number, and so on. To ensure that each of the classes has these same properties, you could copy the code that creates those properties into each of the files containing your class definitions. As mentioned earlier, though, one of the benefits of an OOP approach is code reuse, so of course you don't need to copy code, but instead can reuse the properties and methods of these classes through a process called inheritance. Inheritance is the capability of a class to assume the methods and properties of a parent class.

Inheritance enables you to define a base class—in this case, Automobile—and say that other classes are a type of Automobile and as such have all the same properties and methods that all Automobiles have. You can say that a Sedan is an Automobile, and the Sedan would automatically inherit everything defined by the Automobile class without your having to copy any code. Then you only need to write the additional properties and methods of the Sedan class that are not shared by all automobiles. The only work left for you to do is define the differences; the similarities between the classes are inherited from the base class.

The capability to reuse code is one benefit, but there's a second major advantage to using inheritance. Let's say you have a class called Customer with a method buyAutomobile. This method would take one parameter, an object of class Automobile, and its internal operations would print the paperwork needed to document the sale and would decrement the car in question from the inventory system. Because all Sedans, PickupTrucks, and MiniVans are Automobiles, you can pass objects of these classes to a function expecting an Automobile. Because the three specific types inherit from the more generic parent class, you know that they will all have the same base set of properties and methods. As long as you only need the methods and properties common to all Automobiles you can accept objects of any class that inherits from Automobile.

Here's another example: cats. All cats share some properties. They eat, sleep, purr, and hunt. They also have shared properties—weight, fur color, whisker length, and running speed. However, lions have a mane of a certain length (at least the male lions do) and they growl. Cheetahs have spots. Common house cats have neither of these things, yet all of these animals are cats.

In PHP you specify that a class is a subset of another by using the keyword extends, which tells PHP that the class you are declaring should inherit all the properties and methods from its parent class, and that you are adding functionality or providing some additional specialization to that class.

Preserving the Functionality of the Parent

Sometimes, you want to preserve the functionality provided by the parent. You don't need to completely override the function, you just need to add something to it. You could copy all the code from the parent method into the subclass' method, but as you've already seen, OOP offers you better ways of doing this than just copying lines of code.

To call the functionality provided by the parent, use the syntax parent::[function name]. To just add additional behavior to a method, first call parent::[function name] and then add your additional code. When extending a function in this way, always call the method on the parent before doing anything else. This ensures that any changes to the operation of the parent won't break your code.

Because the parent class may be expecting the object to be in a certain state, or may alter the state of the object, overwrite property values, or manipulate the objects internal data, always invoke the parent method before adding your own code when extending an inherited method.

Interfaces

There are times when you have a group of classes that are not necessarily related through an inheritance-type relationship. You may have totally different classes that just happen to share some behaviors in common. For example, both a jar and a door can be opened and closed, and are in no other way related. No matter the kind of jar or the kind of door, they both can carry out these activities, but there is no other common thread between them.

In OOP, an interface enables you to specify that an object is capable of performing a certain function but it does not necessarily tell you how the object does so. An interface is a contract between unrelated objects to perform a common function. An object that implements the interface is guaranteeing to its users that it is capable of performing all of the functions defined by the interface specification. Bicycles and footballs are totally different things, yet objects
representing those items in a sporting goods store inventory system must be capable of interacting with that system.

By declaring an interface and then implementing it in your objects, it's possible to hand completely different classes to common functions. The following example shows the door-and-jar analogy.

Encapsulation

As mentioned earlier in this chapter, objects enable you to hide the details of their implementation from users of the object. For example, a user doesn't need to know whether the Volunteer class stores information in a database, a flat text file, an XML file, or other data storage mechanism to be able to invoke the signUp method. Similarly, he doesn't need to know whether the information about the volunteer contained within the object is implemented as single variables, an array, or even another object. This ability to hide the details of implementation is known as encapsulation. Generally speaking, encapsulation refers to these two concepts: the protection of a class's internal data from code outside that class, and the hiding of the details of implementation.

The word encapsulate literally means to place in a capsule, or outer container. A well-designed class provides a complete outer shell around its internals and presents an interface to code outside the class that is wholly separated from the particulars of those internals. By doing this, you gain two advantages: you can change the implementation details at any time without affecting code that uses your class and, because you know that nothing outside your class can inadvertently modify the state or property values of an object built from your class without your knowledge, you can trust the state of the object and the value of its properties to be valid and to make sense.

You'll recall from earlier in the chapter that the member variables of a class and its functions have a visibility, which refers to what can be seen by code outside the class. Private member variables and functions are not accessible to code outside the class and are used for the class's internal implementation. Protected member variables and functions are only visible to the subclasses of the class. Public member variables and functions are usable by any code, inside or outside of the class.

Generally speaking, all internal member variables of a class should be declared private. Any access needed to those variables by code outside the class should be done through a public method. When someone wants you to try a new food, you don't allow him to insert it directly into your stomach for a good reason—you need to be able to examine the food and determine if it's something you want to allow into your body. The method for giving you food involves handing it to you and allowing you to decide to eat it or not. Similarly, when an object wants to allow code outside of it to change properties or in some other way affect its internal data, by encapsulating access to that data in a public function (and by keeping the internal data private) you have the opportunity to validate the changes and accept or reject them.

For example, if you're building a banking application that handles details of customer accounts, you might have an Account object with a property called totalBalance and methods called makeDeposit and makeWithdrawal. The totalBalance property should be read-only. The only way to affect the balance is to make a withdrawal or a deposit. If the totalBalance property is implemented as a public member variable, you can write code that would increase the value of that variable without having to actually make a deposit. Obviously, this would be bad for the bank. Instead, you implement this property as a private member variable and provide a public method called getTotalBalance, which returns the value of that private member variable. With the variable storing the value of the account balance made private, it can't be manipulated directly. Because the only public methods that affect the account balance are makewithdrawal and makeDeposit, a user has to actually make a deposit if he wants to increase the value of his account.

Encapsulation of internal data and method implementations allows an object-oriented software system to protect and control access to data and to hide the details of implementation, giving you flexible, stable applications.

Changes to OO in PHP5

Support for objects in PHP goes all the way back to PHP3. There was never any intention of supporting the idea of classes or objects, but some limited support was added, almost as an afterthought, to provide "syntactic sugar" (to use Zeev Suraski's phrase) for associative arrays. Object support in PHP was originally designed as a convenient way of grouping data and functions, but only a small subset of the features traditionally associated with a full-blown object oriented-programming language was included.

As PHP grew in popularity, the use of an OO approach became increasingly common in large applications. However, the poor internal implementation became limiting. Most notably, there was no support for real encapsulation. You could not specify member variables or methods to be private or protected. Everything was public which, as you've seen, can be problematic.

Additionally, there was no support for abstract interfaces or methods. Methods and member variables could not be declared static. There were no destructors. All of these are concepts that are familiar to anyone with a background in other object-oriented programming languages and the lack of these features in PHP's object model could make that transition from a language like Java (which does support all of these ideas) to PHP difficult.

PHP5 introduces a number of major changes to the object-oriented features in the language that addresses these problems and others, giving PHP real OO capabilities and providing increased performance when using the OO capabilities:

• Keywords for controlling the visibility of member variables and methods allowing for private, protected, and public members.

• PHP5 provides dereferencing support such that you can write code such $obj->getObj()->doSomething(). In previous versions of PHP, dereferencing support was limited.

• Static methods and class constants are now supported, allowing for greater compile-time and runtime checking. Static methods are invoked with the::operator.

• Unified constructors, using the __construct() method, instead of a method with the same name as the class makes it easier to alter inheritance when multiple classes are involved in a tree of inheritance.

• Classes in PHP can now have destructors, through the __destruct() method. This allows actions to be taken when the object is destroyed

• Support for abstract classes and interfaces has been added. This gives you the capability to define required methods in a parent class while deferring implementation to a subclass. Abstract classes can't be instantiated, only their nonabstract subclasses can.

• Functions and methods can use type hints on their parameters. It's now possible to specify the class for function parameters that are expecting an object. function foo(Bar $objBar) { ... enables you to be sure that the data type of the parameter $objBar will be an object of class Foo.

Summary

In this Post you explored the concept of object-oriented programming. A class was seen as a blueprint for creating objects. Objects are runtime bundles of data and functions created from a class definition. Objects have characteristics, called properties, and behaviors, called methods. Properties can be thought of as variables and methods are functions.
Some classes share a common parent type. When you declare a class to be a subtype of a parent class, it inherits the methods and properties of the parent. You have the option to override inherited methods. You can completely reimplement the method, if you so choose, or continue to use the parent's implementation but also add specializations particular to the subclass (or not override the method at all).

Encapsulation is an important concept in object-oriented programming. It refers to the capability of a class to protect access to its internal member variables and shield users of that class from the particulars of its implementation. There are three levels of visibility of data and functions: private members that can only be used by the class's internal operations; protected members that are visible to subclasses; and public members that be used by code outside the class.

Object-oriented support in PHP received a major overhaul with the introduction of PHP5 and the Zend Engine 2. New features and significant performance improvements make PHP a real OO programming language.




Post a Comment

Previous Post Next Post