Подтвердить что ты не робот

Возврат значения в функции конструктора класса

До сих пор у меня есть класс PHP с конструктором

public function __construct ($identifier = NULL)
{
 // Return me.
if ( $identifier != NULL )
{
  $this->emailAddress = $identifier;
  if ($this->loadUser() )
    return $this;      
  else
  {
// registered user requested , but not found ! 
return false;
  }
}

функциональность loadUser заключается в поиске базы данных для определенного адреса электронной почты. Когда я устанавливаю идентификатор на какой-то адрес электронной почты, я уверен, что это не в базе данных; первый IF будет принят, и переходит к первому ELSE. здесь конструктор должен возвращать FALSE; но вместо этого он возвращает объект класса со всеми значениями NULL!

как я могу это предотвратить? спасибо

EDIT:

Благодарю всех вас за ответы. это было довольно быстро! Я вижу, что путь ООП заключается в том, чтобы выбросить исключение. Так что бросить один, мой вопрос меняется, что я должен делать с исключением? Руководство php.net довольно запутанно!

    // Setup the user ( we assume he is a user first. referees, admins are   considered users too )
    try { $him = new user ($_emailAddress);
    } catch (Exception $e_u) { 
      // try the groups database
      try { $him = new group ($_emailAddress); 
      } catch (Exception $e_g) {
          // email address was not in any of them !!  
        }
    }
4b9b3361

Ответ 1

Конструкторы не получают возвращаемых значений; они служат целиком для создания класса.

Без реструктуризации того, что вы уже делаете, вы можете использовать здесь исключение.

public function __construct ($identifier = NULL)
{
  $this->emailAddress = $identifier;
  $this->loadUser();
}

private function loadUser ()
{
    // try to load the user
    if (/* not able to load user */) {
        throw new Exception('Unable to load user using identifier: ' . $this->identifier);
    }
}

Теперь вы можете создать нового пользователя таким образом.

try {
    $user = new User('[email protected]');
} catch (Exception $e) {
    // unable to create the user using that id, handle the exception
}

Ответ 2

Конструктор должен создать объект. Поскольку в php булевы не считаются объектами, единственным вариантом является null. В противном случае используйте обходной путь, то есть напишите статический метод, который создает фактический объект.

public static function CheckAndCreate($identifier){
  $result = self::loadUser();
  if($result === true){
    return new EmailClassNameHere();
  }else{
    return false;
  }
}

Ответ 3

Конструктор не может вернуть ничего, кроме объекта, который он пытается создать. Если экземпляр не завершился правильно, вы оставите экземпляр класса, полный свойств NULL, как вы обнаружили.

Если объект загружается в неполном или состоянии ошибки, я бы предложил установить свойство, чтобы указать это.

// error status property
public $error = NULL;

public function __construct ($identifier = NULL)
{
 // Return me.
if ( $identifier != NULL )
{
  $this->emailAddress = $identifier;
  if (!$this->loadUser() )
  {
   // registered user requested , but not found ! 
   $this->error = "user not found";
  }
}

При создании экземпляра объекта вы можете проверить, имеет ли он статус ошибки:

$obj = new MyObject($identifier);
if (!empty($obj->error)) {
   // something failed.
}

Другой (возможно, лучший) вариант заключается в том, чтобы выдать исключение в конструкторе и завернуть создание экземпляра в try/catch.

Ответ 4

Лучшее, что вы можете сделать, это то, что предложил Стив. Никогда не создавайте конструкторы, которые выполняют любую другую работу, а затем назначают параметры конструктора свойствам объекта, возможно, создают некоторые по умолчанию, но ничего больше. Конструкторы предназначены для создания полностью функционального объекта. Такой объект должен всегда работать так, как ожидалось, после его создания. У пользователя есть электронная почта, имя и, возможно, некоторые другие свойства. Когда вы хотите создать экземпляр объекта пользователя, дайте все эти свойства его конструктору. Выбросы исключений тоже не очень хорошие. Исключение должно быть выбрано в исключительных условиях. Просить пользователя по электронной почте ничего исключительного, даже если вы в конечном итоге выясните, что такого пользователя нет. Исключение может быть, например, если вы попросите пользователя по электронной почте = '' (если только это не является обычным состоянием в вашей системе, но идентификатор скорее предполагает, что сообщения электронной почты являются нулевыми в этих случаях). Чтобы получить все эти свойства для объекта пользователя, у вас должен быть объект factory (или репозиторий, если вы предпочитаете) (да, объект - это плохая практика использовать статические все) Частный конструктор тоже плохая практика (вам все равно нужен статический метод, и, как я уже сказал, статика очень плохая)

поэтому результат должен быть примерно таким:

class User {
  private $name;
  private $email;
  private $otherprop;

  public function __construct($name, $email, $otherprop = null) {
    $this->name = $name;
    $this->email = $email;
    $this->otherprop = $otherprop;
  }
}

class UserRepository {
  private $db;

  public function __construct($db) {
    $this->db = $db; //this is what constructors should only do
  }

  public function getUserByEmail($email) {
    $sql = "SELECT * FROM users WHERE email = $email"; //do some quoting here
    $data = $this->db->fetchOneRow($sql); //supose email is unique in the db
    if($data) {
      return new User($data['name'], $data['email'], $data['otherprop']);
    } else {
      return null;
    }
  }
}

$repository = new UserRepository($database); //suppose we have users stored in db
$user = $repository->getUserByEmail('[email protected]');
if($user === null) {
  //show error or whatever you want to do in that case
} else {
  //do the job with user object
}

См? без статики, без исключения, простых конструкторов и очень читаемых, проверяемых и модифицируемых

Ответ 5

Почему бы просто не передать результаты в конструктор, необходимый для создания объекта, а не попытаться иногда скомпрометировать конструктор?

Даже если вы иногда можете его терпеть неудачу, вам все равно нужно будет проверить после вызова конструктора, чтобы убедиться, что он действительно построил, и в этих строках вы можете просто вызвать → loadUser() и передать результаты в конструктор.

Хороший намек, который кто-то мне сказал, "всегда дает конструктору то, что ему нужно для создания объекта, не заставляйте его искать его".

public function __construct ($emailInTheDatabase, $otherFieldNeeded)
{
    $this->emailAddress = $emailInTheDatabase;
    $this->otherField = $otherFieldNeeded;
}

Ответ 6

спасибо за все комментарии и решения. вот что я сделал, чтобы исправить проблему: (надеюсь, это поможет другим)

// Setup the user ( we assume he is a user first. referees, admins are considered users too )
    try {
      $him = new user ($_emailAddress); 
      // check the supplied password 
      $pass_ok = $him->auth($_Password);

      // check the activation status 
      $active_ok = $him->makeActive();

    } catch (Exception $e_u) { 
      // try the groups database
      try { 
      $him = new group ($_emailAddress);
      // check the supplied password 
      $pass_ok = $him->auth($_Password);
              //var_dump ($pass_ok);

      // check the activation status 
      $active_ok = $him->makeActive();
      } catch (Exception $e_g) {
          // email address was not in any of them !!
          $pass_ok = false; $active_ok = false;
        }
    }

Ответ 7

Я бы не слишком много вложил в конструкцию. Вы должны рассмотреть статический функционал, который создает пользователя (factory) вместо того, чтобы помещать все в конструктор. Таким образом, вы все равно можете использовать свой пользовательский объект, не требуя неявной функции загрузки. Это избавит вас от боли.

public function __construct(){}

public function setIdentifier($value){
    $this->identifier = $value;
}

public function load(){
    // whatever you need to load here
    //...
    throw new UserParameterNotSetException('identifier not set');
    // ...
    // if user cannot be loaded properly
    throw new UserNotFoundException('could not found user');
}

public static function loadUser($identifier){
    $user = new User();
    $user->setIdentifier($identifier);
    $user->load();
    return $user;
}

Использование образца:

$user = new User(); 
try{
    $user->setIdentifier('identifier');
    $user->load();
}
catch(UserParameterNotSetException $e){
    //...
}
catch(UserNotFoundException $e){
    // do whatever you need to do when user is not found
}

// With the factory static function:
try{
    $user2 = User::loadUser('identifier');
}
catch(UserParameterNotSetException $e){
    //...
}
catch(UserNotFoundException $e){
    // do whatever you need to do when user is not found
}

Ответ 8

Я действительно удивляюсь, что в течение 4 лет ни один из 22-ки зрителей не предложил создать частный конструктор и метод, который пытается создать такой объект:

class A {
    private function __construct () {
        echo "Created!\n";
    }
    public static function attemptToCreate ($should_it_succeed) {
        if ($should_it_succeed) {
            return new A();
        }
        return false;
    }
}

var_dump(A::attemptToCreate(0)); // bool(false)
var_dump(A::attemptToCreate(1)); // object(A)#1 (0) {}
//! new A(); - gives error

Таким образом вы получаете либо объект, либо false (вы также можете вернуть его null). Ловить обе ситуации сейчас очень легко:

$user = User::attemptToCreate('[email protected]');
if(!$user) { // or if(is_null($user)) in case you return null instead of false
    echo "Not logged.";
} else {
    echo $user->name; // e.g.
}

Вы можете проверить это прямо здесь: http://ideone.com/TDqSyi

Я считаю, что мое решение более удобно использовать, чем бросать и ловить исключения.