【问题标题】:PHP - Is this proper use of exceptions for error handling within classes?PHP - 这是在类中正确使用异常处理错误吗?
【发布时间】:2012-04-27 11:53:33
【问题描述】:

我在这个主题上进行了大量搜索,并得到了很多好的(但不同的)结果。有些结果并不完全相关,最终似乎是一个偏好问题,但我感兴趣的是我是否遵循良好的设计原则

如果这个问题太模糊,可以删除它,但你能推荐我在哪里发布吗?

此外,这只是一个示例。这里有很多事情我通常会做不同的事情,但为了简单起见,我这样做了。

代码很长,但您应该可以直接将其复制并粘贴到一个新的 PHP 文件中并在您的环境中运行它;无需设置。

具体问题

  • 这是使用异常并在调用方处理它们的正确方法吗?
  • 我什至应该为此使用例外吗?
  • 我的骨架自定义异常是否正确?

代码

您可以在单独的窗口here 中查看副本。我会把它贴在这里。保存它并在您的环境中运行它,它应该可以按原样运行,无需任何修改:

注意:前面的代码很长

<?php

    error_reporting ( E_ALL | E_STRICT );




    class MemberLoginException extends Exception
    {

        public function __construct ( $message = null, $code = 0, Exception $previous = null )
        {
            parent::__construct ( $message, $code, $previous );
        }

    }




    class AccountsInsertException extends Exception
    {

        public function __construct ( $message = null, $code = 0, Exception $previous = null )
        {
            parent::__construct ( $message, $code, $previous );
        }

    }




    class AccountsManager
    {

        protected $_accounts = array ();
        protected $_lcUsernames = array ();     # all usernames in lowercase for checking if username is taken



        public function __construct ( array $accounts = null )
        {
            $this->setAllAccounts ( $accounts );
        }



        public function __destruct ()
        {
            unset ( $this->_accounts, $this->_lcUsernames );
        }



        public function __toString ()
        {
            $return = '';

            if ( count ( $this->_accounts ) > 0 ) :

                $return = '<table>';
                $return .= '<tr><th>Username</th><th>Password</th></tr>';

                foreach ( $this->_accounts as $account ) :

                    $return .= 
                    '<tr>
                        <td>'. htmlentities ( $account['username'], ENT_QUOTES, 'UTF-8' ) . '</td>
                        <td>'. htmlentities ( $account['password'], ENT_QUOTES, 'UTF-8' ) . '</td>
                    </tr>';

                endforeach;

                $return .= '</table>';

                return $return;
            endif;
        }



        public function Clear ()
        {
            $this->_accounts = array ();
            $this->_lcUsernames = array ();
        }



        public function Authenticate ( Member $member )
        {
            $username = strtolower ( $member->getUsername () );

            if ( count ( $this->_accounts ) ) :

                foreach ( $this->_accounts as $account ) :

                    if ( strtolower ( $account['username'] ) == $username )
                        return ( bool ) ( $account['password'] == $member->getPassword () );

                endforeach;

            else :
                return false;
            endif;
        }



        public function getAllAccounts ()
        {
            return $this->_accounts;
        }



        public function setAllAccounts ( array $newValue = null )
        {
            if ( is_null ( $newValue ) )
                $this->_accounts = array ();
            else
                $this->_accounts = $newValue;
                $this->_lcUsernames = array ();

                foreach ( $this->_accounts as $account )
                    $this->_lcUsernames[] = strtolower ( $account['username'] );

            return $this;
        }



        public function hasAccount ( $username )
        {
            return in_array ( strtolower ( $username ), $this->_lcUsernames, false );
        }



        public function AddAccount ( $username, $password )
        {

            /*
            Faster to be redundant by storing a lowercase copy of the username for comparison

            if ( array_key_exists ( strtolower ( $username ), array_change_key_case ( $this->_accounts ) ) )
                throw new AccountsInsertException ( 'Unable to create account; account already exists.' );
            */

            if ( $this->hasAccount ( $username ) )
                throw new AccountsInsertException ( 'Unable to create account; account already exists.' );

            $this->_accounts[] = array (
                'username' => $username,
                'password' => $password,
            );

            $this->_lcUsernames[] = strtolower ( $username );
            return $this;
        }



        public function RemoveAccount ( $username )
        {
            if ( $this->hasAccount ( $username ) ) :
                unset ( $this->_accounts[$username] );
                unset ( $this->_lcUsernames [ strtolower ( $username ) ] );
            endif;

            return $this;
        }



        public function __Debug ()
        {
            echo "\r<pre>\r";
            print_r ( $this->_accounts );
            echo "\r</pre>\r\r\r<pre>\r";
            print_r ( $this->_lcUsernames );
            echo "\r</pre>\r\r";
        }

    }




    class Member
    {

        protected $_username = '';
        protected $_password = '';



        public function __construct ( $username, $password )
        {
            $this->setUsername ( $username );
            $this->setPassword ( $password );
        }



        public function getUsername ()
        {
            return $this->_username;
        }



        public function setUsername ( $newValue )
        {
            $this->_username = ( string ) $newValue;
            return $this;
        }



        public function getPassword ()
        {
            return $this->_password;
        }



        public function setPassword ( $newValue )
        {
            $this->_password = ( string ) $newValue;
            return $this;
        }

    }


    # create a new accounts manager which stores all accounts and handles authentication
    # the Member class would be responsible for setting session variables, etc. Manager just checks user/pass.
    $manager = new AccountsManager ();

?><!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />

        <style>

            *
            {
                font-family: "Segoe UI", "Trebuchet MS", Tahoma, Arial, Helvetica, sans-serif;
            }

            body
            {
                margin: 4em 6em;
                line-height: 1.6em;
                font-size: smaller;
            }

            header
            {
                border-bottom: 2px solid #efefef;
                margin-bottom: 3em;
                padding-bottom: 1em;
            }

            h1, h2, h3, h4, h5, h6
            {
                font-weight: normal;
                letter-spacing: 1px;
                color: royalblue;
            }

            h5, h6
            {
                font-weight: bold;
            }

            header h1 sub, header h1 sup
            {
                font-size: small;
                color: #FF4400;
                letter-spacing: 2px;
            }

            section
            {
                border-bottom: 1px dotted #ccc;
                padding-bottom: 2em;
                margin-bottom: 3em;
            }

            table
            {
                border: 1px solid #eee;
                padding: 1em;
                border-right-width: 2px;
                border-bottom-width: 2px;
            }

            th
            {
                text-align: left;
                font-variant: small-caps;
                border-bottom: 1px dotted #ccc;
                padding-bottom: .75em;
                margin-bottom: .75em;
                letter-spacing: 1px;
                color: #FF4400;
            }

            td:hover
            {
                background-color: skyblue;
            }

            td
            {
                margin: 0;
                display: table-cell;
                padding: .5em;
            }

            pre
            {
                font-family: "Droid Sans Mono", Consolas, "Courier New", Courier, monospaced;
                border: 1px solid #E4E4E4;
                padding: 1em;
                line-height: 1em;
            }

            .error
            {
                color: red;
                border: 1px dotted #ccc;
            }

            .success
            {
                color: forestgreen;
                border: 1px dotted #e0e0e0;
            }

            .error, .success
            {
                padding: .75em;
                background-color: #FFFFCC;
                border: 1px solid #E4E4E4;
            }

        </style>

        <title>Sample Login System - Test Exceptions</title>
    </head>

    <body>

        <header>
            <h1>Simple Login System <sup>demonstrating exceptions&hellip;</sup></h1>
        </header>



        <section>
            <h2>No database required</h2>

            <p>To avoid time setting up your environment, this test simply uses a class that stores an array of accounts.
            Obviously, this isn't persistent (at this time) and it doesn't actually save anything anywhere except in the
            array during the script's lifetime. Upon the next request, the previous accounts will be erased.</p>
        </section>



        <section>
            <h2>Creating accounts...</h2>

            <?php

                $createList =
                    array (

                        array (
                            'username' => 'Daniel Elkins',
                            'password' => 'delkins[not-pass-for-anything]',
                        ),

                        array (
                            'username' => 'Jennifer Lynn',
                            'password' => 'lilJenn',
                        ),

                        array (
                            'username'=> 'Charlie Dog',
                            'password'=> 'grrrrr',
                        ),

                    );

                if ( $manager->setAllAccounts ( $createList ) instanceof AccountsManager ) : ?>

                    <p><strong>Accounts were created successfully!</strong> They should be listed in
                    a table below.</p>

                <?php

                else :

                ?>

                    <p class="error">There was an error creating your accounts...</p>

                <?php

                endif;

            ?>

        </section>


        <section>
            <h2>List of accounts</h2>

            <?php echo $manager; ?>

        </section>


        <section>
            <h2>Trying to create one that already exists...</h2>

            <?php

            try
            {
                $manager->AddAccount ( 'Daniel Elkins', 'delkins[not-pass-for-anything]'); ?>

                <p class="success">Account created successfully!</p>

                <?php

            }
            catch ( AccountsInsertException $exception )
            {
                ?>

                <p class="error"><?= $exception->getMessage (); ?></p>

                <?php

            }

            ?>

        </section>


        <section>
            <h2>Showing accounts again</h2>

            <?php echo $manager; ?>

        </section>


        <section>
            <h2>Valid login test</h2>

            <p>Logging in user `Daniel Elkins`&hellip;</p>

            <?php

            if ( $manager->Authenticate ( new Member ( 'Daniel Elkins', 'delkins[not-pass-for-anything]' ) ) ) : ?>

                <p class="success">Authentication successful!</p>

                <?php

            else :

            ?>

                <p class="error">Unable to login; invalid username or password!</p>

                <?php

            endif;

            ?>

        </section>


        <section>
            <h2><strong>Invalid</strong> login test</h2>

            <p>Logging in user `Doesnt_Exist`&hellip;</p>

            <?php

            if ( $manager->Authenticate ( new Member ( 'Doesnt_Exist', '1234' ) ) ) : ?>

                <p class="success">Authentication successful!</p>

                <?php

            else :

            ?>

                <p class="error">Unable to login; invalid username or password!</p>

                <?php

            endif;

            ?>

        </section>


        <section>
            <h2>Debug information</h2>

            <?php $manager->__Debug (); ?>

        </section>

    </body>

</html>

【问题讨论】:

  • 听起来对codereview.stackexchange.com 来说这是一个更好的问题。此外,如果您所做的只是调用 parent:: 中完全相同的方法,则无需重复构造函数声明。
  • 哇,我什至不知道它的存在。我会把它贴在那里,谢谢。

标签: php class exception error-handling design-principles


【解决方案1】:

这是使用异常并在 调用方?

对我来说似乎是一种合理的方法。您正在抛出特定于类的异常,因此很容易捕获或传播它们,并且只抛出特定的异常,而不必捕获所有内容并进行过滤。

我什至应该为此使用例外吗?

如果您认为帐户存在的特殊情况,是的。

我的骨架自定义异常是否正确?

是的,尽管您可以考虑在其中添加元数据,例如在 AccountInsertException 中创建的帐户名称。这可能没有必要,但如果您发现自己处于有用的情况下,这只是需要考虑的事情。

否则代码在某些地方有点乱,但我假设部分原因是示例。

【讨论】:

  • 不是“特殊情况”怎么办?触发器错误()?我的班级如何向主应用程序“报告”当时存在问题?我宁愿不必使用依赖注入并将错误处理程序对象传递给我所有类的构造函数...... try/catch 似乎更优雅和简单。此外,代码是“放在一起”的,在明显的情况下我可以使用 PHP 的内置函数,但究竟什么是“一团糟”?我真的很喜欢你关于修改我的异常类以显示帐户详细信息等的建议。谢谢。
  • 我会避免 trigger_error 因为调用代码不可能以理智的方式处理它。您通常会使用返回值。例如,在查询数据库并返回某些内容的方法中,您通常会在没有找到任何东西而不是抛出的情况下返回 null。混乱:在函数内部构建 HTML,类不遵循推荐的命名/编码样式,诸如此类。我可能让它听起来比现在更糟;)
  • 这是一个仅针对这个问题的示例。无论如何,我认为在 __toString () 方法中返回准系统 HTML 表没有任何问题;这就是表格数据的输出(我认为)。如果它不是 __toString () 我不会那样做。至于命名约定,您指的是哪些“推荐的”?曾?梨?有这么多,没有“正确”的。但是,是的,代码只是为了一个例子而被放在一起,不知道为什么会受到批评。丝毫没有被它打扰,我猜它只是数字。
  • 是的,只是想提一下,以防有什么需要改进的地方:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-15
  • 2011-04-03
  • 2017-05-29
  • 1970-01-01
相关资源
最近更新 更多