tags:

views:

52

answers:

3

Hi there.

I'm trying to build site using OOP in PHP. Everyone is talking about Singleton, hermetization, MVC, and using exceptions. So I've tried to do it like this:

Class building whole site:

class Core
{
    public $is_core;
    public $theme;
    private $db;
    public $language;
    private $info;
    static private $instance;

    public function __construct($lang = 'eng', $theme = 'default')
    {
        if(!self::$instance)
        {
            try
            {
                $this->db = new sdb(DB_TYPE.':host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
            }
            catch(PDOException $e)
            {
                throw new CoreException($e->getMessage());
            }
            try
            {
                $this->language = new Language($lang);
            }
            catch(LangException $e)
            {
                throw new CoreException($e->getMessage());
            }
            try
            {
                $this->theme = new Theme($theme);
            }
            catch(ThemeException $e)
            {
                throw new CoreException($e->getMessage());
            }
        }
        return self::$instance;
    }
    public function getSite($what)
    {
        return $this->language->getLang();
    }
    private function __clone() { }

}

Class managing themes

class Theme
{
    private $theme;
    public function __construct($name = 'default')
    {
        if(!is_dir("themes/$name"))
        {
            throw new ThemeException("Unable to load theme $name");
        }
        else
        {
            $this->theme = $name;
        }
    }
    public function getTheme()
    {
        return $this->theme;
    }
    public function display($part)
    {
        if(!is_file("themes/$this->theme/$part.php"))
        {
            throw new ThemeException("Unable to load theme part: themes/$this->theme/$part.php");
        }
        else
        {
            return 'So far so good';
        }
    }

}

And usage:

error_reporting(E_ALL);

require_once('config.php');
require_once('functions.php');

try
{
    $core = new Core();
}
catch(CoreException $e)
{
    echo 'Core Exception: '.$e->getMessage();
}
echo $core->theme->getTheme();
echo "<br />";
echo $core->language->getLang();

try
{
    $core->theme->display('footer');
}
catch(ThemeException $e)
{
    echo $e->getMessage();
}

I don't like those exception handlers - i don't want to catch them like some pokemons... I want to use things simple: $core->theme->display('footer'); And if something is wrong, and debug mode is enabled, then aplication show error. What should i do?

+1  A: 

If you don't want to catch and want to ignore them, then you might as well don't throw the exceptions (in fact, there's no alternative -- exceptions have to be caught or the script execution will end).

Do something like

if (debug_is_on())
    throw new MyException(...);

Alternatively, you can throw errors instead and then adjust the error_reporting level.

Artefacto
Is it only way? Then I'll still have to try/catch errors, after checking is debug on.
Misiur
If debug is on, you can let the exception unhandled and fix the error.
Artefacto
+1  A: 

I'm not familiar with PHP, but you should definately stop doing pokemon exceptions. First, there should be no need to replace every exception (PDOException) with a specific exception (CoreException). Second, use multiple catch blocks in your usage section, like this:

try
{
   $core->theme->display('footer');
}
catch(ThemeException $e)
{
   echo $e->getMessage();
}
catch(PDOException $e)
{
   echo $e->getMessage();
}

Your class "Core" can then shrink down considerably (no more try/catch for each item). Granted, you will show more catch blocks at a higher level, but that is what you should be doing with OOP and exceptions.

Finally, check to see if there already is an exception super-class for some subset of the exceptions you are trying to catch. That will reduce the number of catch blocks.

Brent Arias
Well, I'm just starting, but this multiple catching shortened my code a lot! I've used CoreException becouse i didn't knwe about this multiple catching
Misiur
A: 

You could consider using a custom error handler and setting it application wide. This is only really a valid approach if you are operating under a design pattern like Front Controller where the handler can be easily set application wide.

Here's an example of the handler I typically use:

<?php
class ErrorHandler
{
    // Private constructor to prevent direct creation of object
    private function __construct()
    {
    }

    /* Set user error-handler method to ErrorHandler::Handler method */
    public static function SetHandler($errTypes = ERROR_TYPES)
    {
        return set_error_handler(array ('ErrorHandler', 'Handler'), $errTypes);
    }

    // Error handler method
    public static function Handler($errNo, $errStr, $errFile, $errLine)
    {

        $backtrace = ErrorHandler::GetBacktrace(2);

        $error_message = "\nERRNO: $errNo\nTEXT: $errStr" .
                     "\nLOCATION: $errFile, line " .
                     "$errLine, at " . date('F j, Y, g:i a') .
                     "\nShowing backtrace:\n$backtrace\n\n";

        // Email the error details, in case SEND_ERROR_MAIL is true
        if (SEND_ERROR_MAIL == true)
        error_log($error_message, 1, ADMIN_ERROR_MAIL, "From: " .
        SENDMAIL_FROM . "\r\nTo: " . ADMIN_ERROR_MAIL);

        // Log the error, in case LOG_ERRORS is true
        if (LOG_ERRORS == true)
        error_log($error_message, 3, LOG_ERRORS_FILE);

        /* Warnings don't abort execution if IS_WARNING_FATAL is false
         E_NOTICE and E_USER_NOTICE errors don't abort execution */
        if (($errNo == E_WARNING && IS_WARNING_FATAL == false) ||
        ($errNo == E_NOTICE || $errNo == E_USER_NOTICE))
        // If the error is nonfatal ...
        {
            // Show message only if DEBUGGING is true
            if (DEBUGGING == true)
            echo '<div class="error_box"><pre>' . $error_message . '</pre></div>';
        }
        else
        // If error is fatal ...
        {
            // Show error message
            if (DEBUGGING == true)
            echo '<div class="error_box"><pre>'. $error_message . '</pre></div>';
            else
            echo SITE_GENERIC_ERROR_MESSAGE;

            // Stop processing the request
            exit();
        }
    }

    // Builds backtrace message
    public static function GetBacktrace($irrelevantFirstEntries)
    {
        $s = '';
        $MAXSTRLEN = 64;
        $trace_array = debug_backtrace();

        for ($i = 0; $i < $irrelevantFirstEntries; $i++)
        array_shift($trace_array);
        $tabs = sizeof($trace_array) - 1;

        foreach ($trace_array as $arr)
        {
            $tabs -= 1;
            if (isset ($arr['class']))
            $s .= $arr['class'] . '.';
            $args = array ();

            if (!empty ($arr['args']))
            foreach ($arr['args']as $v)
            {
                if (is_null($v))
                $args[] = 'null';
                elseif (is_array($v))
                $args[] = 'Array[' . sizeof($v) . ']';
                elseif (is_object($v))
                $args[] = 'Object: ' . get_class($v);
                elseif (is_bool($v))
                $args[] = $v ? 'true' : 'false';
                else
                {
                    $v = (string)@$v;
                    $str = htmlspecialchars(substr($v, 0, $MAXSTRLEN));
                    if (strlen($v) > $MAXSTRLEN)
                    $str .= '...';
                    $args[] = '"' . $str . '"';
                }
            }

            $s .= $arr['function'] . '(' . implode(', ', $args) . ')';
            $line = (isset ($arr['line']) ? $arr['line']: 'unknown');
            $file = (isset ($arr['file']) ? $arr['file']: 'unknown');
            $s .= sprintf(' # line %4d, file: %s', $line, $file);
            $s .= "\n";
        }

        return $s;
    }
}
?>

You can then define a few easy constants to determine some basic behaviors.

//determines if the application will fail on E_WARNING level errors.
define('IS_WARNING_FATAL', true);
//determines error message shown
define('DEBUGGING', true);
// The error types to be reported
define('ERROR_TYPES', E_ALL);

// Settings about mailing the error messages to admin
define('SEND_ERROR_MAIL', false);
define('ADMIN_ERROR_MAIL', '[email protected]');
define('SENDMAIL_FROM', '[email protected]');


//Saves errors to a file when true
define('LOG_ERRORS', false);
define('LOG_ERRORS_FILE', 'path-to-error-log');
/* Generic error message to be displayed instead of debug info
 (when DEBUGGING is false) */
define('SITE_GENERIC_ERROR_MESSAGE', '<h1>An unknown error occurred!  Our webmaster has been notified.</h1>');
DeaconDesperado