Error Handling for Dynamic Twitter Signature Images with PHP

Over the course of my past two articles I’ve been showing you how to build a script capable of creating and displaying a dynamic signature image containing the latest status from a user’s Twitter feed. In the third installment in this series, I will be demonstrating how to add proper object-oriented error handling to the SignatureImage class.

Before we begin, let’s take a moment to examine the fully operational class.

class SignatureImage

{

    private $screen_name;

    private $profile_image;

    private $status_text;

    private $local_avatar;

   

    public function __construct($name, $bg_image, $adir)

    {

        $this->fetchUserInfo(strtolower($name));

        $this->fetchAvatar($this->profile_image, $adir);

        $this->renderImage();

    }

 

    private function fetchUserInfo($name)

    {

        $url = "http://twitter.com/statuses/user_timeline/{$name}.xml?count=1";

        $xml = $this->curlRequest($url);

        if ($xml === false) {

            // User feed unavailable.

        }

        $statuses = new SimpleXMLElement($xml);

        if (!$statuses || !$statuses->status) {

            // Invalid user channel.

        }

       foreach ($statuses->status as $status) {

            $this->status_text   = (string) $status->text;

            $this->profile_image = (string) $status->user->profile_image_url;

            $this->screen_name   = (string) $status->user->screen_name;

            break;

        }

    }

 

    private function curlRequest($url)

    {

        if (!extension_loaded(‘curl’)) {

            // PHP extension CURL is not loaded.

        }

        $curl = curl_init($url);

        curl_setopt($curl, CURLOPT_HEADER, false);

        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

        $result = curl_exec($curl);

        if (curl_errno($curl) !== 0 || curl_getinfo($curl, CURLINFO_HTTP_CODE) !== 200) {

            $result === false;

        }

        curl_close($curl);

        return $result;

    }

 

    private function fetchAvatar($url, $adir)

    {

        $parts      = explode(‘/’, $url);

        $fname      = end($parts);

        $adir       = preg_match(‘#^(.*?)/$#i’, $url) ? $adir : "{$adir}/";

        $fname      = $adir . $fname;

        if (!file_exists($fname)) {

            $img = $this->curlRequest($url);

            $fp = fopen($fname, ‘w’);

            fwrite($fp, $img);

            fclose($fp);

        }

        $this->local_avatar = $fname;

    }

}

 

    private function renderImage($bg_image)

    {

        if (!function_exists(‘gd_info’)) {

            // No GD support.

        }

        $margin_left = 11;

        $margin_bottom = 6;

        $image = @imagecreatefromjpeg($bg_image);

        if (!$image) {

            // Unable to create image.

        }

        $this->embedAvatar($image, $this->local_avatar, $margin_left, $margin_bottom);

        $string = "{$this->screen_name}: {$this->status_text}";

        $this->embedText($image, $string, $margin_left, $margin_bottom);

        header("Content-type: image/jpeg");

        $created = @imagejpeg($image);

        if (!$created) {

            // Unable to finalize image.

        }

        @imagedestroy($image);

    }

 

    private function embedAvatar(&$image, $avatar, $left, $bottom)

    {

        if (file_exists($avatar)) {

            if (stristr($avatar, ‘.jpg’)) {

                $aimg = @imagecreatefromjpeg($avatar);

            } elseif (stristr($avatar, ‘.png’)) {

                $aimg = @imagecreatefrompng($avatar);

            }

            if (!$aimg) {

                // Unable to create avatar.

            }

            $awidth  = imagesx($aimg);

            $aheight = imagesy($aimg);

            $top     = imagesy($image) – $aheight – $bottom;

            $copied  = @imagecopy($image, $aimg, $left, $top, 0, 0, $awidth, $aheight);

            if (!$copied) {

            // Unable to add avatar to image.

            }

        }

    }

 

    private function embedText(&$image, $text, $left, $top, $tmaxw=300, $font=3)

    {

        $fheight = imagefontheight($font);

        $color   = imagecolorallocate($image, 100, 100, 100);

        $lines   = $this->lineWrap($font, $text, $tmaxw);

        $lmax    = floor(imagesy($image) / $fheight);

        $lcount  = (count($lines) > $lmax) ? $lmax : count($lines);

        $ttop    = (imagesy($image) – ($lcount*$fheight)) / 2;

        $tleft   = 2*$left + 48;

        while ((list($num, $line) = each($lines)) && $num < $lcount) {

            imagestring($image, $font, $tleft, $ttop, $line, $color);

            $ttop += $fheight;

        }

    }

 

    private function lineWrap($font, $text, $maxwidth)

    {

        $fwidth  = imagefontwidth($font);

        if ($maxwidth != NULL) {

            $maxcharsperline = floor($maxwidth / $fwidth);

            $text = wordwrap($text, $maxcharsperline, "n", 1);

        }

        return explode("n", $text);

    }

}

 

isset($_GET['name']) or die(‘You must provide a user name.’);

 

new SignatureImage($_GET['name'], ‘banners/my_banner.jpg’, ‘avatars’, ‘cache’);

This class already employs pretty thorough error-catching.  Throughout the code I’ve used comments to indicate error states.  Today we’ll be replacing those comments with actual error-handling code in order to make this a fully functional OOP class.

{mospagebreak title=Understanding error-handling}

Before we dive too far into today’s new code, it’s important that you understand the error-handling mechanisms available to you in PHP 5.  To do that, you’ll need to familiarize yourself with PHP’s Exception class.

The Exception class provided by PHP is used to throw Exceptions in your application whenever errors occur.  Those exceptions may then be caught and handled in a variety of ways according to your application’s needs.  Let’s examine a very simple example of the use of a PHP exception.

$x = 0;

try {

    if $x == 0 {

        throw new Exception(‘Cannot divide by 0′);

    }

    $y = 100 / $x;

}

catch (Exception $e) {

    echo $e->getMessage();

}

This examines attempts to divide the 100 by some number in the variable $x.  I’ve created an error state by setting $x equal to 0, since you cannot divide any number by 0.  To prevent PHP from displaying an error for this division, I’ve surrounded the process in an If statement that first determines whether or not $x contains the value 0.  To this point, this should all be fairly standard procedure since this is a classic example of error prevention.

Next, I’ve employed the use of exceptions.  Whenever $x equals 0 this code will throw an exception with the message “Cannot divide by 0.”  This is done using the throw statement.  Notice the use of the “new” keyword.  We want to throw a new instance of the Exception class while passing it a string that contains the error message.

This is sufficient to throw an Exception in PHP.  However, we can’t actually do anything with that exception yet.  For this to be effective, the code needs to be contained in a Try block.  This tells PHP to be on the lookout for possible exceptions.

Whenever PHP encounters an exception within a Try block, it will halt execution of the Try block and look for the first Catch block to catch and handle the exception.  In this case, the catch block simply writes the error message back.  This is a very simple demonstration.  You’ll see the true power of exceptions as you see them used in our application.

{mospagebreak title=Throwing exceptions for errors}

Let’s move back through our code and replace all of the error comments with throw statements.

    private function fetchUserInfo($name)

    {

        $url = "http://twitter.com/statuses/user_timeline/{$name}.xml?count=1";

        $xml = $this->curlRequest($url);

        if ($xml === false) {

            // User feed unavailable.

        }

        $statuses = new SimpleXMLElement($xml);

        if (!$statuses || !$statuses->status) {

            // Invalid user channel.

        }

       foreach ($statuses->status as $status) {

            $this->status_text   = (string) $status->text;

            $this->profile_image = (string) $status->user->profile_image_url;

            $this->screen_name   = (string) $status->user->screen_name;

            break;

        }

    }

We first encounter error states in the fetchUserInfo method.  It has two different error states. One occurs when the user feed is unavailable, and the other occurs when the script encounters an invalid user channel.

    private function fetchUserInfo($name)

    {

        $url = "http://twitter.com/statuses/user_timeline/{$name}.xml?count=1";

        $xml = $this->curlRequest($url);

        if ($xml === false) {

            throw new Exception(‘User feed unavailable.’);

        }

        $statuses = new SimpleXMLElement($xml);

        if (!$statuses || !$statuses->status) {

            throw new Exception(‘Invalid user channel.’);

        }

       foreach ($statuses->status as $status) {

            $this->status_text   = (string) $status->text;

            $this->profile_image = (string) $status->user->profile_image_url;

            $this->screen_name   = (string) $status->user->screen_name;

            break;

        }

    }

The conversion process is very easy.  Simply replace all of the comments with throw statements.  The comment itself works very nicely as the error message string for the Exception class.  For the sake of space I won’t show you every instance, but you can follow this simple example to complete each of the remaining replacements.

{mospagebreak title=Playing throw and catch}

As I mentioned previously, throw statements don’t do much good in PHP without a catch statement.  Knowing where to place them is dependent upon your application.  Since this particular application produces a single output (a single Twitter signature image), it’s convenient to catch all of the exceptions in a single location.  The class can then either produce an image or not.

The class’s constructor houses the function calls for the entire image creation process.  This makes it a convenient spot to place my try and catch blocks.  Quite simply, we can wrap the entire contents of the constructor in a try block.  Since each of the exception throws throughout our code are called as the result of a function call in the constructor, we never have to worry about having an unhandled exception.  This approach also keeps the entire error-handling process completely contained within the class itself.  This is a great approach for larger applications that may contain a great number of different classes.

    public function __construct($name, $bg_image, $adir)

    {

        try {

            $this->fetchUserInfo(strtolower($name));

            $this->fetchAvatar($this->profile_image, $adir);

            $this->renderImage();

        }

        catch (Exception $e) {

            die($e->getMessage());

        }

    }

If at any point during execution, the method calls in the constructor encounter an exception, execution of the method will stop and code execution will jump to the catch block.  This results in a call to PHP’s die function, which displays the error message provided by the throw statement that generated the exception before ending code execution.

In this article you learned how to implement PHP’s exceptions as a means of error-handling for our Twitter Signature Image class.  But don’t go away just yet.  In the next article in this series I will show you how to create your own custom exception class to further take control of how errors are handled in PHP by creating a replacement image whenever errors occur.  Until next time, keep coding!

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri Ensest hikaye