Restituzione di un valore nella function di costruzione di una class

Finora ho una class PHP con il constructor

 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; } } 

la funzionalità di loadUser è di cercare il database per un particolare indirizzo email. Quando ho impostato l'identificatore su un messaggio e-mail che sono sicuro che non sia nel database; il primo IF viene passato e passa al primo ELSE. qui il constructor dovrebbe restituire FALSE; ma invece, restituisce un object della class con tutti i valori NULL!

come posso evitare questo? Grazie

MODIFICARE:

Grazie a tutti per le risposte. è stato abbastanza veloce! Vedo che il modo OOP è di lanciare un'exception. Quindi a tiro, la mia domanda cambia che cosa dovrei fare con l'exception ?? il manuale di php.net è piuttosto confuso!

  // 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 !! } } 

I costruttori non ottengono valori di return; servono interamente per istanziare la class.

Senza ristrutturare ciò che stai già facendo, potresti considerare di usare un'exception qui.

 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); } } 

Ora puoi creare un nuovo utente in questo modo.

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

Il constructor suppone di creare un object. Poiché in booleani php non vengono considerati oggetti, l'unica opzione è nulla. Altrimenti usa una soluzione alternativa, cioè scrivi un metodo statico che crea l'object reale.

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

Un constructor non può restituire altro che l'object che sta tentando di creare. Se l'istanziazione non viene completata correttamente, ti verrà lasciata un'istanza di class piena di properties; NULL come hai scoperto.

Se l'object viene caricato in uno stato incompleto o di errore, suggerirei di impostare una properties; per indicarlo.

 // 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"; } } 

Quando si crea un'istanza dell'object, è ansible controllare se ha uno stato di errore:

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

Un'altra (forse migliore) alternativa è lanciare un'exception nel constructor e avvolgere l'istanza in un try/catch .

Il meglio che puoi fare è ciò che Steve ha suggerito. Non creare mai costruttori che eseguono un lavoro diverso dall'assegnare i parametri del constructor alle properties; dell'object, magari creare alcuni predefiniti, ma nient'altro. I costruttori hanno lo scopo di creare un object completamente funzionale. Un tale object deve sempre funzionare come previsto dopo la sua istanziazione. Un utente ha email, nome e probabilmente altre properties;. Quando si desidera creare un'istanza di un object utente, fornire tutte queste properties; al suo constructor. Anche le eccezioni di lancio non sono un buon modo. Un'exception deve essere lanciata in condizioni eccezionali. Chiedere un utente via e-mail non è niente di eccezionale, anche se si scopre che non esiste un tale utente. L'exception potrebbe essere ad esempio se si chiede un utente tramite e-mail = '' (a less che non si tratti di uno stato regolare nel proprio sistema, ma l'id piuttosto che i messaggi di posta elettronica siano nulli in questi casi). Per get tutte queste properties; per un object utente dovresti avere un object factory (o un repository se preferisci) (sì, un object – è una pratica sbagliata usare qualsiasi cosa) Il constructor privato è una ctriggers pratica (tu ho bisogno comunque di un metodo statico e, come ho già detto, la statica è pessima)

quindi il risultato dovrebbe essere qualcosa del genere:

 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 } 

Vedere? nessuna statistica, nessuna exception, semplici costruttori e molto leggibile, testabile e modificabile

Perché non passare semplicemente i risultati al constructor necessario per build l'object, piuttosto che provare a far fallire il constructor a volte?

Anche se a volte potresti fallire, dovrai comunque controllare dopo aver chiamato il constructor per assicurarti che lo abbia effettivamente realizzato, e in quelle linee, puoi semplicemente call -> loadUser () e passare i risultati nel constructor.

Un buon suggerimento che qualcuno mi ha detto: "Dai sempre al constructor ciò di cui ha bisogno per build l'object, non farlo andare a cercarlo".

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

grazie per tutti i commenti e le soluzioni. ecco cosa ho fatto per risolvere il problema: (spero che aiuti gli altri)

 // 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; } } 

Non metterei troppo nel costrutto. Dovresti considerare una function statica che crea l'Utente (fabbrica) invece di mettere tutto nel constructor. Pertanto, è ancora ansible utilizzare l'object utente senza wherer call implicitamente la function di caricamento. Questo ti farà risparmiare dolore.

 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; } 

Esempio di utilizzo:

 $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 } 

Sono davvero sorpreso che per 4 anni nessuno dei 22k viewer abbia suggerito di creare un constructor privato e un metodo che tenta di creare un object come questo:

 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 

In questo modo si ottiene un object o falso (è ansible anche renderlo restituito null). Catturare entrambe le situazioni è ora molto semplice:

 $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; // eg } 

Puoi testarlo qui: http://ideone.com/TDqSyi

Trovo la mia soluzione più comoda da usare rispetto al lancio e alla cattura di eccezioni.