私はこのトピックについてたくさん検索しましたが、多くの良い (しかし異なる) 結果が得られました。一部の結果はあまり関係がなく、最終的には好みの問題のようですが、良い設計原則に従っているかどうかに興味があります.
これがあまりにも漠然とした質問である場合は、遠慮なく削除してください。
また、これは一例です。ここには、通常とは異なる方法で行うことがかなりありますが、簡単にするために、このようにしました。
コードは長いですが、それを 1 つの新しい PHP ファイルに直接コピー & ペーストして、環境で実行できるはずです。セットアップは必要ありません。
具体的な質問
- これは、例外を使用して呼び出し側で処理する正しい方法ですか?
- これには例外を使用する必要がありますか?
- スケルトンのカスタム例外は正しいですか?
コード
こちらの別ウィンドウでコピーを表示できます。ここに貼り付けます。保存して環境で実行すると、変更なしでそのまま機能するはずです。
注意: 先の長いコード
<?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…</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`…</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`…</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>