У меня есть класс подключения, который инициализирует учетные данные базы данных, когда я __construct
Это.
Всякий раз, когда это не удается, генерируется исключение, что учетные данные не могут быть установлены, поскольку файл пуст или переменные не установлены.
Переменные не установлены сейчас.
Но я все еще могу вызывать объект для вызова других функций внутри этого класса, чего я не хочу, потому что это невозможно без переменных. Как это:
$connection = new Connection(); //Causes exception because variables arent set
$connection->initialize(); //Should not be ran, because the variables arent set. Application shouldnt continue aswell.
$connection->doFurtherThings(); //Wich shouldnt be run aswell, because the application couldnt go further without a db connection
Какова причина этого, пока я поймал исключение и не дал инициализировать значения?
public function __construct() {
try {
require "Configuration.php";
$credentials = new Configuration('config.ini'); //Doesnt matter. just sets the configuration file
$credential = $credentials->getItems(); //Gets the items
if (isset($credential['engine'], $credential['host'], $credential['dbname'], $credential['username'], $credential['password'])) {
$this->engine = $credential['engine'];
$this->host = filter_var($credential['host'], FILTER_VALIDATE_IP);
$this->dbname = $credential['dbname'];
$this->username = $credential['username'];
$this->password = $credential['password'];
} else {
throw new Exception("Login credential's arent not set");
}
} catch (Exception $e) {
echo $e->getMessage();
}
}
Должен ли я die()
приложение самостоятельно, внутри catch(Exception)
? Я думал, что исключение сделало это.
Я вижу, что ты делаешь.
Вы пытаетесь использовать Объект конфигурации. Это потрясающе, и это именно то, что вы должны делать. Однако то, как вы это делаете, не самый лучший способ.
Прежде чем вы даже получить к коду, который использует объект конфигурации, вы должны создать свой объект конфигурации и проверить, что тот Объект полностью настроен и действительный прежде чем пытаться использовать объект внутри другого. Это не обязанность объекта-потребителя проверять данные из внутреннего в контексте вашей системы здесь.
Прежде всего, ваш Credentials
объект. Мы создаем здесь интерфейс, который говорит, что «Любой объект учетных данных должен иметь validate()
метод, и этот метод вызывает исключение, если учетные данные являются недействительными.
interface Credentials
{
/**
* @throws CredentialsValidationException
*/
public function validate();
}
Почему там validate()
метод? Потому что ты не следует помещать бизнес-логику в конструктор вашего объекта. Будущие разработчики знают, что они могут позвонить validate()
а также тот сообщит им, имеет ли объект действительные учетные данные.
Теперь перейдем к вашей конкретной конфигурации. В этом Configuration
объект, вы утверждаете: «Чтобы иметь действительный объект, пользователь должен укажите хост, имя базы данных, движок, имя пользователя и пароль.
class Configuration implements Credentials
{
protected $host;
protected $engine;
protected $dbName;
protected $username;
protected $password;
/**
* We're NOT validating here, we're just stating that this object requires
* these parameters to become an actual object
*/
public function __construct($host, $engine, $dbName, $username, $password)
{
$this->host = $host;
$this->dbName = $dbName;
$this->engine = $engine;
$this->username = $username;
$this->password = $password;
}
/**
* As our Credentials interface requires, validate everything
*
* {@inheritDoc}
*/
public function validate()
{
// Check this is a valid object
// Consider using a Validation object passed in via Dependency Injection
// If it's not a valid object, throw a CredentialsValidationException
}
}
Теперь мы перенесли ответственность за наличие действительных учетных данных в Configuration
(Credentials
) сам объект. Следующим шагом является использование этого объекта.
class Connection
{
protected $credentials;
/**
* @param Credentials $credentials
*/
public function __construct(Credentials $credentials)
{
$this->credentials = $credentials;
}
}
В вашем Connection
объект, вы заявляете, что вам требуется любой объект, который реализует Credentials
интерфейс. Так что вы не только получаете возможность использовать полиморфизм здесь вы отделяете конфигурацию приложения от ваших классов (то, что вы пытались сделать изначально).
Вы также сейчас используете Внедрение зависимости; Передача объектов через конструкторы / методы для использования классом-потребителем. Это означает, что ваш код отделен, и вы можете использовать эти объекты в любом месте приложения или, если хотите, в совершенно другой библиотеке.
Вот объектный API, который вы теперь можете использовать:
$credentials = new Configuration('host', 'engine', 'dbname', 'user', 'pass');
try
{
$credentials->validate();
$connection = new Connection($credentials);
// @todo Whatever else you want to do
}
catch (CredentialsValidationException $e)
{
// @todo Log the error here with a logger object (check out monolog)
// @todo Make sure the user viewing the code gets a nice response back
}
Если хотите сила действительный Connection
объект, просто позвоните Configuration::validate()
в методе, в котором вы его используете (но не в конструкторе). Вы можете использовать фабрику для создания объекта и заставить вызывать validate для вас. Делай, что хочешь!
На заметку о смерти, не умирай в приложении. Сделайте то, что вам нужно, чтобы отладчик (вы) и пользователь (вы или кто-то еще) по-разному узнали о том, что не так. Обычно вы входите в систему для разработчика и даете сообщение для пользователя. Поймать исключение и повторить, в чем проблема.
Заключительная записка, это один способ сделать это. Вы могли бы использовать Validation
объект. Вы могли бы сделать validate()
становиться isValid()
вместо этого просто верните true / false. Вы могли бы сделать Connection
вызов объекта validate()
/ isValid()
— это зависит от вашей архитектуры и того, что вы хотите сделать. Дело в том, что вы разделили эти два класса и использовали лучшие практики одновременно.
Заключительная мысль — убедитесь, что вы добавили phpdocs, как я сделал в своем коде. Будущий разработчик не захочет тебя убивать. И я предлагаю проверить IDE, которая бросает небольшие уведомления, как PhpStorm, когда вы делаете что-то глупое в вашем коде.
Как я сказал в комментариях, это должно быть решение вызывающей стороны, как обращаться с исключением, а не с вызываемым классом.
В вашем конструкторе вы должны выбросить исключение, если что-то пойдет не так, и оставить вызывающий класс, чтобы решить, как обращаться с этим исключением.
public function __construct() {
require "Configuration.php";
$credentials = new Configuration('config.ini'); //Doesnt matter. just sets the configuration file
$credential = $credentials->getItems(); //Gets the items
if (isset($credential['engine'], $credential['host'], $credential['dbname'], $credential['username'], $credential['password'])) {
$this->engine = $credential['engine'];
$this->host = filter_var($credential['host'], FILTER_VALIDATE_IP);
$this->dbname = $credential['dbname'];
$this->username = $credential['username'];
$this->password = $credential['password'];
} else {
throw new Exception("Login credential's arent not set");
}
}
И теперь его вызывающее решение, что делать в случае исключения, например, остановить выполнение, например:
try {
$connection = new Connection(); //Causes exception because variables arent set
$connection->initialize(); //Should not be ran, because the variables arent set. Application shouldnt continue aswell.
$connection->doFurtherThings();
} catch (Exception $e) {
exit($e->getMessage()); // Login credential's arent not set
}
Чтобы лучше проиллюстрировать это, я написал вам простой пример и как примечание, вы должны действительно узнать, как работает поток выполнения.