Introduction to PHP's Object Orientation

Updated pritaeas 12 Tallied Votes 1K Views Share

Introduction

In this introduction I will try to explain basic object orientation (focused on PHP). It is by no means meant to be a complete guide. There are a lot of concepts I am avoiding for simplicity’s sake. I will try to give a theoretical explanation first, and later on provide some code examples.

What is it?

Object orientation is a way of reflecting objects in the real world. If you have a problem that can be described by interacting objects, there’s a good chance an object oriented approach will help you. It is by no means the holy grail, not every problem requires an OO solution.

Suppose you need to build an access control system. In the real world you’d have at least the following objects available: keycards and cardlocks. So if you need to build something like it, why not use a method that will represent these objects?

Terminology

In the following paragraphs I’ll try to provide a short theoretical explanation and example for some key concepts in object orientation.

Classes

A class is basically a blueprint for an object you are modeling. Like a blueprint, it defines what goes where, how you will be able to use it and how big it’s going to be.

Example: The blueprint for a keycard will define it’s as big as a credit card (physical dimension), that it will contain a magnetic stripe (data track) able to contain information, and a method for retrieving that information.

Properties

Properties (or fields) are used for data storage (and often behaviour control). If you read about object orientation and encounter “Encapsulation”, this will be it’s simplest example. A class “encapsulates” data in the form of properties.

Example: Properties of a keycard can be: a data track, the manufacturer, it’s color.

Methods

Methods (functions) are actions on an object. They can request certain information from an object, or tell an object to perform a certain action.

Example: A cardlock will need to be able to read keycard data, and open and close the actual attached lock.

Visibility

Visibility is a way of access control, determining who can use a specific property or method. Some information is public (everyone can see), other information is protected (we’ll get to that in the examples) or private (only the object knows).

Example: You can see a keycard’s track, size and color (public). You cannot see what material it is actually made of (private).

Objects

An object is an instance of a class. Just think of it as: a keycard’s blueprint is the class, but the actual constructed keycard is the object. You can see that a single class (blueprint) can be used to construct multiple objects (keycards). The objects (keycards) may have the same class (blueprint), but they don’t have to be identical: two keycards with the same blueprint will most likely have different unique identifiers (production codes).

Constructor

A constructor defines initial processing when the object is instantiated.

Example: A newly constructed keycard will be imprinted with a unique identifier.

Destructor

A destructor defines final (cleanup) processing when the object is about to be destroyed.

Example: Before destroying a cardlock you will need to detach it from the actual attached lock.

Inheritance

Inheritance is the method of making a new class (child- or sub-class) based on an existing one (parent- or super-class), thus changing or extending the original behaviour.

Example: A new keycard with an added chip will still behave as it’s predecessor with the magnetic stripe, but will have additional storage on the chip and will need a method of retrieving that information.

Method Overriding

Method overriding is a way of changing the functionality of a specific function, but keeping the name and parameters intact.

Example: A cardlock will validate a keycard differently in newer models, because security is improved by using a different encryption method.

What else?

Like diafol said: “taking your procedural code, wrap a class around it and make everything public is NOT object orientation”. If you want to change your procedural code to OO, rethink your code. See if you can write down the separate objects you are using, what data they contain and how they should interact.

The key guideline here is that every class should have one clear purpose. Building a class containing all you need is not the right way to go about it. You should not build an SQL wrapper class that will retrieve your users, calculate your statistics and write the actions to a text file for logging. You should build an SQL class, a user class, a statistics class and a logging class. It is more work initially, but swapping in new functionality (think inheritance and method overriding) will prove much easier. Each class will have a specific purpose, that can more easily be reused in your next OO project, without having to start over from scratch time and time again.

When I first learned OO it was considered very bad practice to use public properties for example. It was considered the right way to provide a public method to retrieve or change the private property (this is where the terms getter and setter come from, not discussed here). In most languages today you can choose more than just a public property, you can make it read-only for example. This feature is not yet available in PHP, but there’s a work-around (not discussed here).

Further Reading

Example Code

If you have any questions on the code below, please ask. It's quite possible I've overlooked some explanations. Don't hesistate to ask.

<pre>
<?php
    // Test class KeyCard
    class KeyCard {
        // Public properties can be read and written by everyone.
        // In this case, the colour can be changed by anyone.
        public $colour;

        // Protected properties can be read and written by
        // the class itself, and any sub-class.
        // The information on the track is not visible by the outside world.
        protected $track;
        protected $version;

        // Private properties can be read and written by the class only.
        // The only way these properties could be read or written by somebody else,
        // is by exposing the information through a method.
        private $manufacturer;

        // This is the default definition for the constructor.
        function __construct() {
            // $this in a class method refers to the current class instance,
            // which is the object you will be working with at that point.

            // The initial colour will be white.
            $this->colour = 'white';

            // The manufacturer of this class will be me.
            $this->manufacturer = 'pritaeas';

            // The track will be empty when you create a new object of this class.
            $this->track = '';

            // The version of this class will be 1.
            $this->version = 1;
        }

        public function readManufacturer() {
            // Since the track property is not readable by anyone,
            // the readTrack function will return it's value.
            return $this->manufacturer;
        }

        public function readTrack() {
            // Since the track property is not readable by anyone,
            // the readTrack function will return it's value.
            return $this->track;
        }

        public function readVersion() {
            // Since the version property is not readable by anyone,
            // the readVersion function will return it's value.
            return $this->version;
        }

        public function writeTrack($newData) {
            // Since the track property is not writable by anyone,
            // the writeTrack function will set it's new value.
            $this->track = $newData;

            // Usually these functions perform additional validation.
            // If the new data is not valid, the track property should not be changed
            // and an exception should be raised, or the return value should be false.

            // Return true to indicate the writing of the track was succesful.
            return true;
        }
    }

    // Usage
    $keyCardObj = new KeyCard();

    // To access properties and methods from an object
    // you need to use the arrow notation (->).

    // These properties are visible trough their methods,
    // calling a method, like a function, uses parenthesis.
    echo "Keycard information:\n";
    echo sprintf("Manufacturer = [%s]\n", $keyCardObj->readManufacturer());
    echo sprintf("Version = [%d]\n", $keyCardObj->readVersion());

    // This property can be accessed,
    // like a variable, it is accessed without parenthesis.
    echo sprintf("Colour = [%s]\n", $keyCardObj->colour);

    // A public property can also be changed
    $keyCardObj->colour = 'red';

    // Show the new colour.
    echo sprintf("Colour = [%s]\n\n", $keyCardObj->colour);

    // Test class CardLock
    class CardLock {
        protected $version;

        function __construct() {
            $this->version = 1;
        }

        public function swipeCard(KeyCard $keyCard) {
            // Simulate a card swipe.
            // The parameter must be a KeyCard object.
            $track = $keyCard->readTrack();

            // We should now validate the track data,
            // and if it is valid open the lock.
            if ($this->validate($track)) {
                $this->openLock();
            }
            else {
                $this->showError();
            }
        }

        protected function openLock() {
            // Stub method that should open the lock,
            // for a certain period of time.
            echo "Lock open.\n";
        }

        protected function validate($data) {
            // Stub method that should validate the track data,
            // and see if this card is allowed access.

            // Any non empty track will be valid.
            return !empty($data);
        }

        protected function showError() {
            // Stub method that should blink the card lock's
            // indicator lights to show that the card was rejected.
            echo "Card not accepted.\n";
        }
    }

    // Usage
    $cardLockObj = new CardLock();

    // Let's swipe our keycard and see what happens.
    echo "Swipe keycard:\n";
    $cardLockObj->swipeCard($keyCardObj);

    // Since the keycard's track is empty by default,
    // the keycard is rejected.

    // Write some data.
    $keyCardObj->writeTrack('TRACKDATA');

    // Let's try again.
    echo "\nSwipe keycard:\n";
    $cardLockObj->swipeCard($keyCardObj);

    // Test class ChipCard
    // All public and protected properties and methods from the parent
    // will be available in this class too.
    class ChipCard extends KeyCard {
        protected $chip;

        function __construct() {
            // This call will call the parent's constructor,
            // inheriting all what is set.
            parent::__construct();

            // Since the parent's version is 1, increase it.
            // The version property is protected, so we can access it here.
            $this->version++;

            // Set the chip's default value to an empty string.
            $this->chip = '';
        }

        public function readChip() {
            return $this->chip;
        }

        public function writeChip($data) {
            $this->chip = $data;
            return true;
        }
    }

    // Usage
    $chipCardObj = new ChipCard();

    echo "\nChipcard information:\n";
    echo sprintf("Manufacturer = [%s]\n", $chipCardObj->readManufacturer());
    echo sprintf("Version = [%d]\n", $chipCardObj->readVersion());
    echo sprintf("Colour = [%s]\n", $chipCardObj->colour);

    // Test class ChipLock
    // All public and protected properties and methods from the parent
    // will be available in this class too.
    class ChipLock extends CardLock {
        // This property will control the new lock's behaviour.
        // When true it reads the chip, when false the track.
        public $chipMode;

        function __construct() {
            // This call will call the parent's constructor,
            // inheriting all what is set.
            parent::__construct();

            // Since the parent's version is 1, increase it.
            // The version property is protected, so we can access it here.
            $this->version++;

            $this->chipMode = true;
        }

        public function enterChip(ChipCard $chipCard) {
            if ($this->chipMode) {
                $data = $chipCard->readChip();
                if ($this->validate($data)) {
                    $this->openLock();
                }
                else {
                    $this->showError();
                }
            }
            else {
                // Return false if the mode is set to swipe
                $this->showError();
            }
        }

        public function swipeCard(KeyCard $keyCard) {
            if ($this->chipMode) {
                // Swipe not allowed in chip mode.
                $this->showError();
            }
            else {
                // Call the swipeCard method from the parent class.
                parent::swipeCard($keyCard);
            }
        }
    }

    // Usage
    $chipLockObj = new ChipLock();

    // Let's enter our chipcard and see what happens.
    echo "\nEnter chipcard:\n";
    $chipLockObj->enterChip($chipCardObj);

    // Since the keycard's chip is empty by default,
    // the chipcard is rejected.

    // Write some data.
    $chipCardObj->writeChip('CHIPDATA');

    // Let's try again.
    echo "\nEnter chipcard:\n";
    $chipLockObj->enterChip($chipCardObj);

    // If we set the mode to track,
    // then the chip must fail, and the old keycard will succeed
    $chipLockObj->chipMode = false;

    // Try the chipcard again
    echo "\nEnter chipcard:\n";
    $chipLockObj->enterChip($chipCardObj);

    // Swipe the keycard
    echo "\nSwipe keycard:\n";
    $chipLockObj->swipeCard($keyCardObj);
?>
</pre>
adam.adamski.96155 commented: Excellent article, makes it easy for me to visualise and apply to my own work, thanks for your effort. +3
cereal commented: well done! +9
diafol commented: smashing! +14
LastMitch commented: This is astonishing! +5
pixelsoul commented: great detail, and nice example +5
Stuugie commented: Thanks for this tutorial prit, it's one of the best OO tuts I've seen. +4
Member Avatar for diafol
diafol

That IS a really nice tut pritaeas. I just wish I'd had something as clear as this when I tried to get my head around OOP. I found the example of the keycard very useful - too many tuts try to provide simple ideas like animal parent classes and cat or dog child classes, which are pretty amusing, but ultimately pointless. I found the early mention of parent::__construct() and overrides with the clear exemplification of subclassing and the mention of "one class - one job" particularly good.

Excellent.

Member Avatar for LastMitch
LastMitch

Does this code works on Codeigniter or other frameworks? This is pretty neat if I can test it out on Codeigniter. I have Codeigniter on my server but never got a chance to play around with Codeigniter.

pritaeas 2,194 ¯\_(ツ)_/¯ Moderator Featured Poster

Thanks to all for the kudos. It was a pain to write, so am glad it is appreciated.

@LastMitch: I am not familiar with CodeIgniter, but the code works. Am sure there is a way to incorporate this in any of the frameworks, unless I am misunderstanding your question.

Member Avatar for LastMitch
LastMitch

@pritaeas

Thanks to all for the kudos. It was a pain to write, so am glad it is appreciated.

Thanks for your hard work! This is a special topic! OOP guideline!

I am not familiar with CodeIgniter, but the code works. Am sure there is a way to incorporate this in any of the frameworks, unless I am misunderstanding your question.

No, you answer my question. OOP runs on Codelgniter it's just that I'm very new to this topic that why I was answering.

pixelsoul 272 Red Pill Featured Poster

This looks good Pritaeas. I started ways back, so moving my through process away from procedural and then trying to wrap my head around OOP has not been an easy task.

Thanks for taking the time to write it up. I'll take some time later and read through it's entirety.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.