PHP OOP: Objects Under The Hood

PHP OOPO Under The Hood

PHP OOP Under The Hood

Programming languages help us solve everyday problems and express our solutions in multiple facets. PHP offers us the ability to develop our solutions using OOP (Object Oriented Programming), as well as the ability to develop those same solutions via a procedural style of programming. I would love to take a good long look under the hood at just how PHP objects and classes do the work that they do, and hope that you could benefit from that knowledge.

What you may wonder is how PHP OOP differs from other languages — if you do indeed come from a background of other OOP-rich languages like Java, Ruby, or Python. When does using this paradigm of programming make sense and when does it not? How do objects work in PHP on a microscopic-level? What are some of the greatest strengths of PHP OOP and some of its biggest weaknesses? Why is PHP OOP different and why shouldn’t that matter? These are all some of the many questions that come across my desk, on a regular basis, from developers and beginner PHP enthusiasts that I’ve worked with over the years, and are some of the key points this article attempts to help you answer.

As a brief recap; last time I discussed PHP arrays, providing a look at what goes on behind the scenes. This was meant to help give you an idea of what you should know, that you may not be fully aware of — from a client perspective. It should have also given you a broader understanding of just why PHP arrays work the way that they do, and how they differ from other languages. Today I’d like to give you a tour, under the hood, of PHP objects in a similar manner. The idea is to help you get a sense of what’s going on deep within the heart of the gigantic engine that is PHP; a close look between the tiny crevices of the gears, cranks, and pistons of our PHP engine — if you will. This is not intended as a general explanation of how PHP objects or PHP OOP work. Rather, I assume that the reader already has a basic understanding of PHP OOP features, so that we may pop open the hood and start examining the mechanics of it all, right away.

The Class Gives Birth to an Object

So let’s just get started without further delay. The best way we’re going to learn about the mechanics of objects is to get our hands dirty. We’re going to need to roll up our sleeves and pull apart those gears. That means it’s time to get some grease under our fingernails!

Every object starts off with the class definition. A class is what gives birth to our object instance and it’s required in order to instantiate an object. So, here’s a very flat look at a PHP object from a client perspective.

class User {
  public $name, $dob, $gender, $occupation;

  public function __construct($name, $dob, $gender, $occupation) {
    $this->name = $name;
    $this->dob = $dob;
    $this->gender = $gender;
    $this->occupation = $occupation;
  }

  public function getAge() {
    $born = new DateTime($this->dob);
    $age = $born->diff(new DateTime)->format('%y');
    return $age;
  }

  public function getBlurb() {
    $info = "$this->name is a {$this->getAge()} year old $this->gender $this->occupation.";
    return $info;
  }
}

$user1 = new User("Larry Page", "1973-03-26",
                  "male", "Computer Scientist");
$user2 = new User("Robert Metcalfe", "1946-04-07",
                  "male", "Computer Scientist");
$user3 = new User("Carol Bartz", "1948-08-28",
                  "female", "Business Executive");

echo $user1->getBlurb(), "\n",
     $user2->getBlurb(), "\n",
     $user3->getBlurb(), "\n";
Larry Page is a 39 year old male Computer Scientist.
Robert Metcalfe is a 66 year old male Computer Scientist.
Carol Bartz is a 64 year old female Business Executive.

As you can see our object appears to be this tightly coupled set of variables and functions that are easy to replicate and can act independent of one another. The premise is that the object’s properties are stored in an designated part of memory for each individual object (using a hash table). That way multiple objects can act on independent parts of memory sharing the same functions (commonly referred to as methods).

This is because every object is created from a class. Think of a class like the blueprints to a building. You can actually build more than one building using the same blueprints. Every building is unique in that they are each their own individual structures. So destroying, modifying, or creating one building does not directly have a consequence on any other building. Similarly, the class can have properties that are specific to it and the object can equally have properties that are specific to each individual object instance. Object variables are called properties in PHP and are also commonly referred to as members. On the other hand, class variables are always static in PHP and belong to the class (also stored in their own hash table). So they can be shared by any object or accessed from any scope if they are public. They do not require an object instance.

The object, however, is fundamentally just an instance of a class. Meaning our objects directly affect both space and time. Space, being our scope (where we created the object instance) and time being our state (in which finite state of the code execution does the object cause a side effect). Though the objects themselves are not necessarily affected by scope or state. The reason for this is that objects act independent of them internally, but appear to be under the direct influence of these factors from the client’s perspective. Let’s examine some of the details as to why.

Symbol Tables

The way that we access an object is through an identifier. On the surface this identifier is normally represented by a variable, but underneath it’s something else. In order to understand the difference between a variable and an object we need to examine something called a symbol table. A symbol table is basically just a hash table (we discussed hash tables in our last talk on PHP Arrays) of identifiers that point to what PHP calls a ZVAL (the internal data structure that represents a PHP variable). PHP keeps track of multiple symbol tables throughout the runtime that represent symbols from many different parts of your code. The main one we want to focus on right now is the global symbol table since we’re using an example in the global scope.

Global Symbol Table
Hash Symbol ZVAL Pointer
0 user1 0x7f236177002d
1 user2 0x67abd1a51d65
2 user3 0xd6f727a0932

This very simple layout demonstrates what the symbol table does for us in our code. PHP has a way to look up the symbol (or variable name) in the symbol table and finds a memory address to the corresponding PHP value (or ZVAL). When we look at the ZVAL we can determine if it’s type is an object. If so the object is represented by what’s called a   zend_object_value  , which is an internal name for the data structure PHP uses to identify and store PHP objects.

We can see the code for this data structure in   Zend/zend_types.h   on line   56–59   of the PHP source.

typedef struct _zend_object_value {
    zend_object_handle handle;
    const zend_object_handlers *handlers;
} zend_object_value;

As you can see the structure is made up of two members. The first, is called   handle   and is of type   zend_object_handle  . That’s an unsigned integer value that uniquely identifies the object in the global object symbol table. This is how we get to the object from the variable (or ZVAL). It’s also what allows us to pass objects around without having to copy the object. The second member is called   handlers   and is of type   zend_object_handlers  , which is another internal data structure that represents meta data about such things as which methods the object may use, which class the object is an instance of, and what properties the object has.

You can see the output from the following code shows us that each variable we copy does not point to a new object, but instead copies the same   zend_object_value   into each ZVAL for that variable, which points back to the same   zend_object_handle  .

$object = new stdclass;
$var1 = $object;
$var2 = $object;
var_dump($object, $var1, $var2);
object(stdClass)#1 (0) {
}
object(stdClass)#1 (0) {
}
object(stdClass)#1 (0) {
}

Notice that all three variables output object(stdClass)#1 (this is a part of the object handle that uniquely identifies the object stored in memory). It is only when we use the   new   or   clone   keywords that PHP will allocate additional memory and assign a new object handle (thereby creating an entirely new object).

So the variable is just a way to get to the object stored in memory. It doesn’t accurately represent the physical memory that stores the object. This is the part where state comes into play. If you were to instantiate an object within the local scope of a function, for example, you would think that the object is cleaned up by the garbage collector once you return from the local scope. At least that’s how variables are cleaned up from the local scope of a function in PHP once the function returns.

The truth is that’s not quite how it works with an object. This is where things get a little tricky and they aren’t obvious to the user. The object doesn’t actually live in the function’s local scope, but the ZVAL that contains a handle to this object does. So the way the garbage collector knows to remove an object from memory is when there are no more   zend_object_value   structs left pointing to that unique object handle. So if we were to return the variable that stores that handle, to the newly instantiated object, back to the calling scope we would have another copy of the zend_object_value and the object would not be destroyed at the point we return from the function.

Object Handlers

To dive a little deeper into the mechanics let’s examine what the object handlers member does. The object handlers tells us exactly how to assemble the object so that it works the way that it should. It’s not just as simple as identifying the methods/functions in the class definition and storing them in the object handlers. Remember that classes can be inherited and as such there is a necessary ancestry chain that we must analyze in order to determine how the object will be used at run time.

For instance, take the following example where we attempt to extend our User class from the previous example.

class AdminUser extends User {
  private $access_level = 1;

  public function setAccess($access_level) {
    $this->access_level = $access_level;
  }
  
  public function getBlurb() {
    $info = "$this->name is a {$this->getAge()} year old $this->gender $this->occupation who is also an admin with access level: {$this->getAccess()}.";
    return $info;
  }
  
  public function getAccess() {
    return $this->access_level;
  }
}

$admin = new AdminUser("John Doe", "1970-01-01",
                       "male", "Code Slinger");
echo $admin->getBlurb();
John Doe is a 42 year old male Code Slinger who is also an admin with access level: 1.

As you can see the object instance we instantiated from the AdminUser class not only inherits methods from its parent, but properties as well. By overloading the getBlurb method in the AdminUser class we cause the object handlers to overwrite the preexisting parent method with our new one. Adversely, the inherited methods remain intact.

This isn’t the whole story though. The object handler also stores information about the class itself. Such as the class name, what file the class was defined in, and on what lines of the code that class definition resides in that file. This is also how PHP knows when to invoke the autoloader when you instantiate an object, or attempt to access a class, that has not already been loaded and how PHP keeps track of class ancestry for each object.

We can see what the object handlers struct looks like in   Zend/zend_object_handlers.h   on line   116–145   in the PHP source.

struct _zend_object_handlers {
  /* general object functions */
  zend_object_add_ref_t               add_ref;
  zend_object_del_ref_t               del_ref;
  zend_object_clone_obj_t             clone_obj;
  /* individual object functions */
  zend_object_read_property_t         read_property;
  zend_object_write_property_t        write_property;
  zend_object_read_dimension_t        read_dimension;
  zend_object_write_dimension_t       write_dimension;
  zend_object_get_property_ptr_ptr_t  get_property_ptr_ptr;
  zend_object_get_t                   get;
  zend_object_set_t                   set;
  zend_object_has_property_t          has_property;
  zend_object_unset_property_t        unset_property;
  zend_object_has_dimension_t         has_dimension;
  zend_object_unset_dimension_t       unset_dimension;
  zend_object_get_properties_t        get_properties;
  zend_object_get_method_t            get_method;
  zend_object_call_method_t           call_method;
  zend_object_get_constructor_t       get_constructor;
  zend_object_get_class_entry_t       get_class_entry;
  zend_object_get_class_name_t        get_class_name;
  zend_object_compare_t               compare_objects;
  zend_object_cast_t                  cast_object;
  zend_object_count_elements_t        count_elements;
  zend_object_get_debug_info_t        get_debug_info;
  zend_object_get_closure_t           get_closure;
  zend_object_get_gc_t                get_gc;
};

The   get_class_entry   member of the object handler helps us identify the information necessary to depict how the object will behave. This is also how built-in classes like Reflection allow us to manipulate the guts of an object (its classes and methods) at run time.

So What Is $this?

Have you ever wondered exactly what is $this and what does it do internally? The answer is that $this is a pseudo variable that PHP uses to put the object instance into the calling context. If you come from a background of Python or C++, for example, you would accomplish the same thing by passing a variable into the constructor of the class (usually self by convention) and that variable would populate as the instance of the class. PHP makes this a little more autonomous and self-serving for you so that you don’t need to take that extra step. Whenever we attempt to access a method from object context PHP automatically makes $this available with the instance of the class.

When you invoke methods on an object PHP has to do some context switching. That’s where we move the active symbol table so that local functions/methods in the class can utilize it and store variables in a local scope without affecting the calling scope. This is also how things like method chaining and passing objects into global functions becomes possible. Let’s build on our previous example using the AdminUser class to see what happens when we pass an object to a global function and conduct operations on that object from within the function’s local scope and how that affects our global scope.

function ObjectFunc($object) {
  $object2 = $object;
  $object2->name = "Foo Bar";
  return $object2;
}

$admin = new AdminUser("John Doe", "1970-01-01",
                       "male", "Code Slinger");
var_dump($admin);

var_dump(ObjectFunc($admin));

echo $admin->getBlurb();
object(AdminUser)#1 (5) {
  ["access_level":"AdminUser":private]=>
  int(1)
  ["name"]=>
  string(7) "John Doe"
  ["dob"]=>
  string(10) "1970-01-01"
  ["gender"]=>
  string(4) "male"
  ["occupation"]=>
  string(12) "Code Slinger"
}
object(AdminUser)#1 (5) {
  ["access_level":"AdminUser":private]=>
  int(1)
  ["name"]=>
  string(7) "Foo Bar"
  ["dob"]=>
  string(10) "1970-01-01"
  ["gender"]=>
  string(4) "male"
  ["occupation"]=>
  string(12) "Code Slinger"
}
Foo Bar is a 42 year old male Code Slinger who is also an admin with access level: 1.

Notice that inside ObjectFunc‘s local scope, the object is not copied even though we copy the object that was passed to the function to a new variable that only resides in the function’s local scope. Again, that’s the effect of us copying the same   zend_object_handle   that we discussed earlier. As long as there’s at least one ZVAL pointing to this handle the garbage collector will not clean up the memory for that object. So it actually doesn’t matter where the object was instantiated as long as some part of our code is referencing that object handle. You should also notice that since they are the same object the modifications we make from within the function remain when we call on the getBlurb method of $admin from the global scope.

This makes passing objects around between scopes a seamless process since we never have to worry about accidentally copying the object when we copy the variable. This gives us that reference-like behavior we saw when we talked about creating array values using references last time, and although PHP objects are not references they certainly have referencey behavior (in that multiple variables can all share the same value). Though this behavior is actually new in PHP 5. Back in the days of PHP 4 objects were copied by value. There’s a reason for why that is and this brings us to our next topic.

PHP OOP Was an Afterthought

If you’ve already been programming in PHP for many years and you’re well accustomed to writing procedural code you should already know that pretty much anything you can do with PHP’s OOP features you can also do with procedural code. OOP just allows us to approach the same solutions to any given problem a little bit differently. Perhaps you found that your code base is easier to read and understand or even to maintain when it’s written in an object oriented fashion. Or, perhaps you find it easier to abstract away complex logic when you’re writing code in an object oriented style. The point is that OOP is only one way to approach the solution to your programming problem. It’s certainly not the only way and PHP has done without it for years before it was ever introduced into PHP 4.

The truth is even during PHP 4, OOP was little more than an afterthought. It was in fact only after PHP 4 that PHP realized the implementation to OOP features weren’t proving to be as easy as they had first seemed. PHP had to reintroduce the internal object APIs in PHP 5 in order to make objects really behave like objects that people were used to in other languages.

This is also where the debate of — why doesn’t PHP make everything into objects — stems from. It’s not that you can’t make scalar values in PHP into objects. It’s actually very much possible with the underlying internal APIs to accomplish this feat, but it’s also not necessary. You can already use objects as Arrays in PHP, for example the ArrayAccess interface is already equipped to utilize the underlying APIs for creating dimensions within objects. So instead of using an object operator   ->   like you normally do with an object, you can use array syntax to make the object behave as though it were an array.

class ArrayObject implements ArrayAccess {
    private $array = array();
    public function offsetSet($offset, $value) {
        if (is_null($offset)) {
            $this->array[] = $value;
        } else {
            $this->array[$offset] = $value;
        }
    }
    public function offsetExists($offset) {
        return isset($this->array[$offset]);
    }
    public function offsetUnset($offset) {
        unset($this->array[$offset]);
    }
    public function offsetGet($offset) {
        return isset($this->array[$offset]) ? $this->array[$offset] : null;
    }
}
$arrayObject = new ArrayObject;
$arrayObject[] = 1;
$arrayObject[] = 2;
var_dump($arrayObject[0],$arrayObject[1]);
int(1)
int(2)

Doing similar for things like strings, ints, etc… isn’t something PHP doesn’t already have the infrastructure to support internally. It would just be a matter of what benefit does it really provide over the existing implementation and can someone take the time to provide an implementation that works. There’s also the concern of BC (Backwards Compatibility).

PHP tries to provide what it’s user base demands and what it’s core developers are keen to spend time implementing. If a particular feature grabs the interest of a core developer they’ll likely pursue it and if there’s enough demand for it — it will eventually make its way into PHP. That’s how we got OOP in the first place.

Lateral vs. Vertical Context Switching

If you enjoy functional programming you’ll likely find many of the same benefits in PHP’s OOP features to be of use to you, like higher order functions or closures (lambdas).

/* Example of a higher order function in PHP */
$exponentiate =
function($base) {
  return function($exponent) use ($base) {
    return pow($base, $exponent);
  };
};

$base = $exponentiate(2);
$result = $base(2);
echo "2^2 = $result\n";

$result = $base(4);
echo "2^4 = $result\n";
2^2 = 4
2^4 = 16

Vertical context switching is when you build up a call stack. When you do recursion, for example, you normally build a vertical call stack as you recursively call the function. It’s simple pointer arithmetic to return from a function as you just return to the same point in the stack you started with before the function was called. With objects you have the ability to do lateral calls, whereby each method called on the object returns an instance of the same type and an additional method may be invoked on that new instance. That means you’re just passing control back and forth between the calling context and the object context.

Taking a look at our previous User class example from earlier we can see that we do some of this method chaining in our getAge method using a DateTime object.

  public function getAge() {
    $born = new DateTime($this->dob);
    $age = $born->diff(new DateTime)->format('%y');
    return $age;
  }

As you can see on line 3 of this code we instantiated a DateTime object and call a method named diff on that object. The method modifies the instance and returns it to the caller. This allows the caller to call another method on the modified object in the same statement. The next method called is format, which returns a string. The string then gets assigned to the caller (our variable $age) and we don’t have to use multiple statements to perform these multiple method calls on the same object.

As we’re calling methods on our object PHP is internally switching the context while the method executes so that when we return to the caller we don’t lose our place in the stack. This is also important for when we throw an exception from within an object, for example. However, this all feels a little different from what you might be used to doing if you were using functions.

For example:

function foo($var) {
  return ++$var;
}

function bar($var) {
  return $var * 2;
}

$var = 1;
echo foo(bar($var)); // 3

What’s different about this approach is that we switch contexts (from caller to calling) with the right-most derivation getting ultimate flow of control. Whereas if you look at our method chaining approach earlier, you have a left-most derivation flow of control. Meaning that here we start by calling the function bar first, before we call on foo. Even though foo shows up in our statement first. To the contrary, we call the method DateTime::diff() on our object before we call DateTime::format(), making the flow of control more obvious in some ways.

Objects from Cradle to Grave

The object is just memory that lasts from the time the object is instantiated (or born) to the time it’s destroyed (may it R.I.P), hence the phrase “cradle to grave”. To give you a broad look at the life cycle of the object let’s take a peculiar example.

function makeObject($value) {
  $object = new stdclass;
  $object->property = $value;
  return $object;
}

function getObject($value) {
  $newObject = makeObject($value);
  return $newObject;
}

function destroyObject($object) {
  unset($object);
}

$myObject = getObject("foo");

destroyObject($myObject);

var_dump($myObject); // Object not destroyed

$obj = $myObject;

unset($myObject);

var_dump($obj); // Same object
object(stdClass)#1 (1) {
  ["property"]=>
  string(3) "foo"
}
object(stdClass)#1 (1) {
  ["property"]=>
  string(3) "foo"
}

The question is when does the object actually get destroyed here? The PHP manual tells you that it’s not until the last reference to an object is destroyed that the object is destroyed, but by reference what we really mean is any ZVAL that still points to the same object handle. Here even though we instantiate the object inside of a function’s local scope the object itself does not reside in any scope. Only the variable does. The fact that we return the variable from one function to another and back to the global scope only means we’re passing around the handle that points to the object we created.

As long as that handle exists PHP knows not to clean up the object memory. So we’re not actually carrying around the object as we move from scope to scope. That would be pretty expensive (the old PHP 4 passing objects by copy behavior). Here we’re just copying around this very light-weight metadata that tells us how to get to the object. Thus removing that handle from memory removes the only link we have to the object (and refcount is the most basic way to do garbage collection).

PHP Object In Memory

If you follow the green arrows, in the above diagram, you’ll see the path our object handle takes from the function’s local scope all the way back to the caller in the global scope. Remember that the variables carry just the handle and not the object and that only the variables are affected by scope. The red arrows show us which object the handle points to (they all point to the same object here). So as long as any one of these handles exist anywhere in memory the object won’t be garbage collected. What happens is as we slowly start to remove one handle at a time throughout each step of this code, we are also making a copy of that handle and passing it somewhere else in the call stack. So there’s always at least one handle left pointing to the object preventing the refcount from dropping to zero.

This is how the object manages to survive from cradle to grave.

The Class Early Binding Problem

PHP has a few edge cases with class inheritance where trying to load classes out of order can cause some weird errors that don’t always make a lot of sense at first.

Let’s take a look at a basic example where we have three classes. There’s a parent class, a child class, and a grand child class. All three classes are defined in the same file in the following order.

class C extends B {}
class B extends A {}
class A {}

The above code would actually issue a fatal error of   Class ‘B’ not found in test.php on line 1  , which doesn’t make a whole lot of sense since we can clearly see that Class B is defined on line 2. The problem is early binding that PHP does when it’s parsing the class definitions. When a class is extending another class, PHP goes looking for the parent class first before it goes looking for the child class. So when we stumble across Class C on line 1 of the above code, we look for class B. The problem is Class B is also a child of Class A. Now we created a problem PHP can’t solve since it has to do this early binding ahead of time.

What may surprise you, though, is this is the only possible order under which this type of error occurs. If we consider all the possible orders that we can define these three classes in there are a total of six possibilities. How we are arrive at that number is by using the formula  n! / (n – r)!  where  n  is the number of classes to choose from and  r  is the number of classes chosen. That gives us  3! / (3 – 3)! = 6 . So you would think that something like the following should also result in the same error.

class B extends A {}
class C extends B {}
class A {}

Or maybe even the following code…

class C extends B {}
class A {}
class B extends A {}

Ironically, it does not. The best thing to do to avoid this weird error is to always define your classes in the order of parent before child. There are a few easy solutions where inheritance may be more complex in your code base or even where you only make decisions of which class needs loaded at run time (e.g. for composition or factory patterns).

One option is to define each class in it’s own file and make sure you include all of your classes at the very beginning of the calling script before you attempt to use them. However, this isn’t always optimal since loading the class definition itself takes up a bit of memory on its own. The least confusing and hassle-free option is to simply rely on an autoloader to load the class definitions for you. You’ll never have this problem if you rely on an autoloader. The autoloader is a fail-safe, of sorts. When PHP can’t find the class you’re trying to use it falls back to invoking the autoloader and seeing if that autoload function can include the necessary class. If the autoload function fails then it becomes a fatal error. This is a way to ensure that you only load the classes you need for that particular request.

Classes and Scope

We know that there is a global scope, in which state becomes possible, and that there is a local scope, in which we can build a call stack. The class is actually neither of these. Classes are essentially statically compiled scopes. They define their own symbol tables at compile time. They can store their own variables, not affected by the global scope.

Here’s an example that might clarify the difference between global scope and class scope.

function foo() {
  global $var;
  $var = 1;
}

// $var doesn't exist yet
var_dump($var);

// foo exports it
foo();
// making it available to the global scope
var_dump($var);

The above code would output something similar to the following…

Notice: Undefined variable: var in ... on line ...
NULL
int(1)

However, a class is not a new scope or even a temporary local scope. It’s a statically compiled scope so all class variables must be initialized at compile time and actually can’t be removed. So the following example would not work.

class Foo {
}
Foo::$var = 1;

The above code would give you a fatal error of   Access to undeclared static property: Foo::$var  . With a class the static variable must be initialized from the class definition or it will not be compiled into the class’s symbol table.

class Foo {
  public static $var;
}
Foo::$var = 1;
var_dump(Foo::$var);
unset(Foo::$var); // this won't work
int(1)
Fatal error: Attempt to unset static property Foo::$var in ... on line ...

The difference here is that the static variable has to be compiled in before a symbol table can be utilized for the class and it can’t be removed at run time. Once this separate class symbol table is constructed the engine can fetch variables from it to pass back to the calling scope.

The same thing happens with static class functions. It’s possible to create a global function in PHP conditionally.

$var = 1;
if ($var) {
  function foo() {
    return 1;
  }
} else {
  function baz() {
    return 0;
  }
}

It’s not possible, however to do this with a class static function. Classes, much like objects, are not bound by scope in the way that functions and variables are, but they do create the presence of a scope since they affect specific parts of memory.

This statically compiled scope is also how we achieve LSB (Late Static Binding).

Class Foo {
  public static function fooFunc($var) {
    // This is the LSB
    return static::sharedFunc($var);
  }
  public static function sharedFunc($var) {
    return $var + $var;
  }
}

Class Bar Extends Foo {
  public static function sharedFunc($var) {
    return $var * $var;
  }
}

echo Bar::fooFunc(10);
100

So essentially, if we did our job right here we should get 100 and not 20 as the output. Note that LSB is available as of PHP 5.3. The reason that we don’t get 20 as the output, even though the function clearly calls a method that is available in both classes, is because we know from the caller which static scope was used to invoke the function. Remember these scopes are compiled in so it’s easy to resolve them at run time. If we were to call Foo::fooFunc(10) directly, we’d get 20 as the output instead.

Wrapping Up

So if you’re of the mindset that PHP OOP is just weird for you or that you’re used to something different take into consideration all of the things we discussed here and how they work in PHP. If you can think of a way to make them work better I would love to hear about it, but once you understand these basic concepts you’ll realize PHP OOP is no more weird than OOP in any other language. These things are by design and they work well when you use them in a systematic way to solve your problem.

What you can take away from objects is that they are easy to pass around to any part of your code and retain their state regardless of where you access them from. That’s because you don’t really pass the object itself around, but instead pass a handle to that object, which is cheaper to copy and less confusing then remembering variable names. The object also abstracts away which functions and properties belong to it since it can work out these rules through the object handlers we discussed earlier.

You have the benefit of a statically compiled class scope, as well, when you create a class. The scope and its contents are already known at compile time so it’s easy to share information from the class with other parts of your code.

There are obviously some confusing edge cases and a few quirky features here and there, but overall PHP OOP isn’t so horrible, or complicated. It’s a great way to tightly couple data and functionality throughout your code.

9 Responses to“PHP OOP: Objects Under The Hood”

  1. Sam Tresler
    December 4, 2012 at 2:59 pm #

    So, there is no way to actually remove an object from memory during a script’s execution? It will defacto just sit there until all the memory is released?

    Or rather, how can I force an object to actually be destroyed, if I no longer need it anywhere?

    • GoogleGuy
      December 6, 2012 at 1:37 am #

      Sure, it’s definitely possible to remove an object from memory. The object is destroyed when the last reference to that object is destroyed. That’s the refcount garbage collection discussed in this article.

  2. Chris
    December 5, 2012 at 12:07 am #

    I enjoy reading all of these articles that you post. Thank you for taking the time to write these, they are VERY informative.

    Please note, the black on blue font for the last local scope picture is hard to read.

  3. December 16, 2012 at 2:12 am #

    Man, this is great! Your knowledges of php internals are incredible, where i can start to learning how php works under the hood ?

  4. Olekhy
    December 17, 2012 at 5:24 am #

    > The above code would actually issue a fatal error of Class ‘B’ not found in test.php on line 2 , …

    error was at line 1

    • GoogleGuy
      December 18, 2012 at 1:49 pm #

      You’re right. That was a bit confusing because I copy/pasted the error message from PHP, but the actual code has an opening PHP tag on line 1.

      :-)

  5. Vegasvikk
    December 20, 2012 at 8:29 pm #

    Wow you write a great explanation for OOP~ thanks a lot. You mind if I post it on LinkedIn PHP group for other people to peruse?

    Cheers,

    Vick

    • GoogleGuy
      December 21, 2012 at 9:12 am #

      Feel free. There’s also an “in share” button on the right-hand side of the page under Socialize. You can link to any of these articles on Google+, LinkedIn, reddit, tubmler, twitter, or anywhere else you’d like. Thanks for reading.

  6. Craig
    June 12, 2013 at 6:13 pm #

    Fantastic article. Been looking for these details for a while. Brilliant keep it up.

Leave a Reply

Your email address will not be published. Required fields are marked *

(Required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>