TUTORIAL: The Magic of __toString()

  • joebert
  • Fart Bubbles
  • Genius
  • User avatar
  • Posts: 13503
  • Loc: Florida

Post 3+ Months Ago

Introduction


You've probably heard of "Magic Methods" if you've done anything at all with OOP in PHP. Most likely the __construct() method which is invoked when initializing an instance of a class you've defined.

While __construct() is unofficially a magic method that is required to be defined when writing classes, __toString() is optional in every sense of the word and is often overlooked in favor of writing methods designed to be invoked for the sole reason of returning a human readable string.

There's nothing wrong with doing that, but if you can get your objects to return a human-readable string without typing anything more than the "$object" reference instead of "$object->method()", why not get it to do that ?

The concepts in this tutorial apply to PHP version 5.2 or newer. Because of limitations in the context that PHP invoked the __toString magic method before PHP 5.2, the examples contained in this tutorial may not work in those older versions.

Examples of Usage


Let's compare an example using both common practice, and using __toString.

First an example using common practice to load a CSV file and print it as an HTML <table>.
PHP Code: [ Select ]
<?php
 
$data_table = new csv_html('file.csv');
echo '<h2>Data Table</h2>' . $data_table->html_table();
 
?>
  1. <?php
  2.  
  3. $data_table = new csv_html('file.csv');
  4. echo '<h2>Data Table</h2>' . $data_table->html_table();
  5.  
  6. ?>


Now let's look at that same example if __toString had been defined in place of the "html_table()" method.
PHP Code: [ Select ]
<?php
 
$data_table = new csv_html('file.csv');
echo "<h2>Data Table</h2>$data_table";
 
?>
  1. <?php
  2.  
  3. $data_table = new csv_html('file.csv');
  4. echo "<h2>Data Table</h2>$data_table";
  5.  
  6. ?>


A more useful example might be anonymous instantiation, where no reference to the object is kept. This is useful where an object is a simple formatter and will likely never be manipulated after instantiation.
PHP Code: [ Select ]
<?php
 
printf('<h2>Data Table</h2>%s', new csv_html('file.csv'));
 
?>
  1. <?php
  2.  
  3. printf('<h2>Data Table</h2>%s', new csv_html('file.csv'));
  4.  
  5. ?>


So How do I Implement __toString() ?


Exactly the same way you define your __construct() method, and any of the other magic methods, with the exception of __autoload().
As a public method within your class definition.

Here's an example, albeit a crude one, of how the class in the CSV example might be written.
PHP Code: [ Select ]
<?php
 
class csv_html
{
    public $filename, $csv;
    public function __construct($filename)
    {
        $this->filename = $filename;
        $this->csv = file_get_contents($filename);
        return $this;
    }
    public function __toString()
    {
        return sprintf(
            '<table><tr><td>%s</td></tr></table>',
            str_replace(array("\n", ','), array('</tr><tr>', '</td><td>'), $this->csv)
        );
    }
}
 
?>
  1. <?php
  2.  
  3. class csv_html
  4. {
  5.     public $filename, $csv;
  6.     public function __construct($filename)
  7.     {
  8.         $this->filename = $filename;
  9.         $this->csv = file_get_contents($filename);
  10.         return $this;
  11.     }
  12.     public function __toString()
  13.     {
  14.         return sprintf(
  15.             '<table><tr><td>%s</td></tr></table>',
  16.             str_replace(array("\n", ','), array('</tr><tr>', '</td><td>'), $this->csv)
  17.         );
  18.     }
  19. }
  20.  
  21. ?>


Now you might ask, "Why not just have the constructor return the HTML?".
Because it would make what will become your parent class constructor useless if you ever tried to extend the class, that's why.

Conclusion


Give __toString() a try, you just might like it.
Moderator Remark: Noted __autoload exception
  • Anonymous
  • Bot
  • No Avatar
  • Posts: ?
  • Loc: Ozzuland
  • Status: Online

Post 3+ Months Ago

  • Rabid Dog
  • Web Master
  • Web Master
  • User avatar
  • Posts: 3245
  • Loc: South Africa

Post 3+ Months Ago

Ah yes and public constructors are generally regarded as evil if the class has any properties that need to be set, hence the Factroy Patterns :)

As for the toString(), I agreed, probably the most under rated, under used method on the planet.

I find using it (unless requirements dictate otherwise) provides a neat method for generating human readable identifiers for the said object.

The other nice thing is things like C# and Java invoke it automatically when bind the objects to UI objects for the UI's Text property.

Nice to see PHP supports it as well :)
  • joebert
  • Fart Bubbles
  • Genius
  • User avatar
  • Posts: 13503
  • Loc: Florida

Post 3+ Months Ago

I believe PHP will throw a fatal error if constructors are defined as anything other than public.

I'm not a big fan of a lot of the design patterns in use today, I cringe any time I read the word "factory". Most of it seems like it just over complicates things to me. :)
  • Rabid Dog
  • Web Master
  • Web Master
  • User avatar
  • Posts: 3245
  • Loc: South Africa

Post 3+ Months Ago

I am a big fan of the patterns :) Especially with runtimes like Java and .NET. Singletons and all tht jazz
  • Rabid Dog
  • Web Master
  • Web Master
  • User avatar
  • Posts: 3245
  • Loc: South Africa

Post 3+ Months Ago

Just a quick follow up on public constructors

Code: [ Select ]
private function __construct(){}


hides the public constructor. No errors, PHP 5.4
  • joebert
  • Fart Bubbles
  • Genius
  • User avatar
  • Posts: 13503
  • Loc: Florida

Post 3+ Months Ago

Interesting. What use is a private constructor? :?
  • Rabid Dog
  • Web Master
  • Web Master
  • User avatar
  • Posts: 3245
  • Loc: South Africa

Post 3+ Months Ago

Prevents initiation using 'new'. You would need a static initialization method.

Code: [ Select ]
MyClass::create()
// instead of
new MyClass()
  1. MyClass::create()
  2. // instead of
  3. new MyClass()


Makes code more readable if class needs to be initialized into a certain state

Code: [ Select ]
MyClass::createDefault()
MyClass::createWith($input)

//etc
  1. MyClass::createDefault()
  2. MyClass::createWith($input)
  3. //etc


Not a fan of parameterised constructors :)

Post Information

  • Total Posts in this topic: 7 posts
  • Moderator: Tutorial Writers
  • Users browsing this forum: No registered users and 3 guests
  • You cannot post new topics in this forum
  • You cannot reply to topics in this forum
  • You cannot edit your posts in this forum
  • You cannot delete your posts in this forum
  • You cannot post attachments in this forum
 
 

© 1998-2014. Ozzu® is a registered trademark of Unmelted, LLC.