2010-11-22 4 views
4

Dies ist ein Problem, das ich ziemlich regelmäßig erfahre und ich habe nie eine Best Practices Situation gefunden. Ausnahmen sind wahrscheinlich der Weg zu gehen, aber die Anwendung, die ich arbeite, macht keinen Gebrauch von ihnen, also versuche ich, bei den derzeit verwendeten Methoden zu bleiben.PHP multiple if/elseif und Fehlerbehandlung/Handhabung Best Practices

Was ist der beste Weg, um Aussagen, Rückgaben, Nachrichten usw. zu erstellen, wenn 3, 4, 5 oder mehr verschiedene Bedingungen überprüft werden müssen und entweder eine Fehlermeldung gesetzt wird oder die Verarbeitung fortgesetzt wird. Ist es am besten, wenn alle Fehler am Anfang des Codes physisch vorliegen?

Hier ist ein Beispiel mit einigen realen Bedingungen.

function process($objectId,$userId,$newData) 
{ 
    $error = ''; 
    if(($object = $this->getObject($objectId)) && $object->userOwnsObject($userId)) 
    { 
     if($this->isValid($newData)) 
     { 
      if($object->isWriteable()) 
      { 
       if($object->write($newData)) 
       { 
        // No error. Success! 
       } 
       else 
       { 
        $error = 'Unable to write object'; 
       } 
      } 
      else 
      { 
       $error = 'Object not writeable'; 
      } 
     } 
     else 
     { 
      $error = 'Data invalid'; 
     } 
    } 
    else 
    { 
     $error = 'Object invalid'; 
    } 
    return $error; 
} 

ODER

function process($objectId,$userId,$newData) 
{ 
    $error = ''; 
    if((!$object = $this->getObject($objectId)) && !$object->userOwnsObject($userId)) 
    { 
     $error = 'Object invalid'; 
    } 
    elseif(!$this->isValid($newData)) 
    { 
     $error = 'Data invalid'; 
    } 
    elseif(!$object->isWriteable()) 
    { 
     $error = 'Object not writeable'; 
    } 
    elseif(!$object->write($newData)) 
    { 
     $error = 'Unable to write to object'; 
    } 
    else 
    { 
     // Success! 
    } 
    return $error; 
} 

Es ist mir klar, dass in diesem Fall der Option 2 ist der Weg zu gehen. Es ist viel klarer. Jetzt können wir machen es ein bisschen komplizierter:

function process($objectId,$userId,$newData) 
{ 
    $error = ''; 
    if(($object = $this->getObject($objectId)) && $object->userOwnsObject($userId)) 
    { 
     $this->setValidationRules(); 
     $parent = $object->getParentObject(); 
     $parent->prepareForChildUpdate(); 

     if($this->isValid($newData,$parent)) 
     { 
      $newData = $this->preProcessData($newData); 

      if($object->isWriteable()) 
      { 
       // doServerIntensiveProcess() has no return value and must be done between these two steps 
       $this->doServerIntensiveProcess(); 

       if($object->write($newData)) 
       { 
        // No error. Success! 
        $parent->childUpdated(); 
       } 
       else 
       { 
        $error = 'Unable to write object'; 
       } 
      } 
      else 
      { 
       $error = 'Object not writeable'; 
      } 
     } 
     else 
     { 
      $error = 'Data invalid'; 
     } 
    } 
    else 
    { 
     $error = 'Object invalid'; 
    } 
    return $error; 
} 

ODER dies die einige Probleme mit ihm hat

function process($objectId,$userId,$newData) 
{ 
    $error = ''; 
    if((!$object = $this->getObject($objectId)) && !$object->userOwnsObject($userId)) 
    { 
     $error = 'Object invalid'; 
    } 
    // Is it wrong to hate multi-line conditionals? 
    elseif(!$this->setValidationRules() || (!$parent = $object->getParentObject()) || 
     !$parent->prepareForChildUpdate() || !$this->isValid($newData,$parent)) 
    { 
     $error = 'Data invalid'; 
    } 
    elseif((!$newData = $this->preProcessData($newData)) || !$object->isWriteable()) 
    { 
     $error = 'Object not writeable'; 
    } 
    // Where does doServerIntensiveProcess() with no return value go?? 
    elseif(!$object->write($newData)) 
    { 
     $error = 'Unable to write to object'; 
    } 
    else 
    { 
     // Success! 
     $parent->childUpdated(); 
    } 
    return $error; 
} 

Ich bin nur nicht sicher, ob der beste Weg, um dieses verschachtelte if-zu handhaben - dann-was-dann-wenn-das-dann-machen-diese Art von Funktionalität. Vielen Dank für Ihre Einsichten!

+0

Sie scheinen zu wissen, dass Ausnahmen der richtige Weg sind, aber warum wollen Sie sie nicht benutzen? Ich verstehe, dass sie in Ihrer derzeitigen Codebasis nicht verwendet werden, aber ich denke, jetzt ist ein guter Zeitpunkt, um damit anzufangen. Die Verwendung einer Rückgabewert-Zeichenfolge, um Fehler anzuzeigen, ist eine absolut schreckliche Idee. – ryeguy

+0

Ja, du hast Recht. Es ist so eine massive Codebase, die auf eine bestimmte Art und Weise gestaltet ist. Ich möchte nicht auf die Zehen treten oder andere Entwickler auf den Gartenweg schicken, wenn sie die Mysterien von Exceptions erforschen. – fred

Antwort

4

Was neige ich dazu, Code sauber zu halten zu tun ist, wie so:

function process($objectId,$userId,$newData) 
{ 
    $object = $this->getObject($objectId); 

    if($object === false) 
    { 
     return "message"; 
    } 

    if($object->userOwnsObject($userId) === false) 
    { 
     return "message"; 
    } 

    if($this->setValidationRules() === false) 
    { 
     return "unable to set validation rules"; 
    } 

    if(false !== ($parent = $object->getParentObject())) 
    { 
     return "unable to get parent object"; 
    } 

    /*... etc ...*/ 

    //if your here the all the checks above passed. 
} 

, indem sie es auf diese Weise Ihre auch die Einsparung von Ressourcen zu tun als Ihre Ihre direkt zurück an Ort und Stelle, der Code sieht sauberer und keine Notwendigkeit für 2 viele Nester

aber wenn Sie bauen ing die Funktion von Grund auf neu Ich sehe nicht, warum Sie Ausnahmen in in Ihrem neuen Code verwenden kann nicht, wird es nicht mit der aktuellen App stören und macht Live einfacher

function process($objectId,$userId,$newData) 
{  
    if(false !== ($parent = $object->getParentObject())) 
    { 
      throw Exception("unable to get parent object"); 
    } 

    /*... etc ...*/ 
} 

und

try 
{ 
    $this->process(....); 
} 
catch(Exception $e) 
{ 
    show_error_page('invalid.php',$e); 
} 

oder eine andere Möglichkeit ist es, eine Fehlerbehandlung Klasse mit einer statischen Methode InternalError wie so

abstract class Error 
{ 
    public static InternalError(Exception $Ex) 
    { 
     Logger::LogException($Ex); 
     //Then flush all buffers and show internal error, 
    } 
} 

so statt des show_error_page genannt erstellen über die Sie tun können:

try 
{ 
    $this->process(....); 
} 
catch(Exception $e) 
{ 
    Error::InternalError($e); //this provides user with an interface to report the error that has just been logged. 
} 

auf diese Weise alle Ihre Exception (n) werden protokolliert und im Administrations-System betrachtet werden kann, das heißt, Sie Fehler schneller und nicht verlassen sich auf Mitglieder sichtbar verfolgen können, um Fehler zu sehen, aber eine nette Entschuldigung mit einer E-Mail erhalten Wenn Sie das Formular formulieren, in dem Sie aufgefordert werden, zu beschreiben, was es versucht hat, wird die Fehler-ID an das Formular angehängt, damit Sie den Benutzer auf den Fehler zurückverfolgen können.

Das ist die beste Form der Fehlerbehandlung IMO.

+0

Danke für die ausführliche Antwort. Ich stimme Ihnen bei den mehrfachen Rückgabewerten und der Fehlerprotokollierung zu. Ich denke, es kommt darauf an, dass ich tun muss, was ich weiß, ist das Beste anstelle der Norm in dieser App. – fred

+0

Wie groß ist die Anwendung, an der Sie arbeiten? – RobertPitt

+0

Ich bin nicht sicher Linien-weise. Ich denke, es sind etwa 6-8 MB Text. – fred

0

Was ist damit?

function process($objectId,$userId,$newData) 
{ 
    if((!$object = $this->getObject($objectId)) && !$object->userOwnsObject($userId)) 
     return 'Object invalid'; 
    elseif(!$this->isValid($newData)) 
     return 'Data invalid'; 
    elseif(!$object->isWriteable()) 
     return 'Object not writeable'; 
    elseif(!$object->write($newData)) 
     return 'Unable to write to object'; 

    // Success! 
} 

Für die komplexeren Beispiel ::

function process($objectId,$userId,$newData) 
{ 
    if(!($object = $this->getObject($objectId)) || !$object->userOwnsObject($userId)) 
     return 'Object invalid'; 

    $this->setValidationRules(); 
    $parent = $object->getParentObject(); 
    $parent->prepareForChildUpdate(); 

    if(!$this->isValid($newData,$parent)) 
     return 'Data Invalid'; 

    $newData = $this->preProcessData($newData); 

    if(!$object->isWriteable()) 
     return 'Object not writable'; 

    // doServerIntensiveProcess() has no return value and must be done between these two steps 
    $this->doServerIntensiveProcess(); 

    if(!$object->write($newData)) 
     return 'Unable to write object'; 

    // No error. Success! 
    $parent->childUpdated(); 
    return ''; 
} 
+1

Der Weg zur Hölle ist mit Code versehen, der Klammern als optional behandelt. Nur zu deiner Information. –

+0

Ich war faul. Und in diesem Fall spielt es keine Rolle, denn es ist nur ein Beispiel. – ThiefMaster

0

würde ich

if($object->isWriteable()) 
{ 
    if($object->write($newData)) 
    { 
     // .. 
    } 
} 

ganz weglassen und Ausnahmen werfen, wenn Objekt-write() (ohne Rückgabewert) stattdessen aufrufen. Das Gleiche gilt für die anderen Beispiele

if ($something->isValid($data)) { 
    $op->doSomething($data); // throws XyException on error 
} 

Wenn Sie wirklich solche Konstrukte verwenden möchten, können Sie auch die swtch -Statement

switch (true) { 
    case !$something->isValid($data): 
     $errors = "error"; 
     break; 
    // and so an 
} 

Aber ich Ausnahmen wirklich empfehlen.

0

Ich denke nicht, dass es per se falsch ist, mehrzeilige Bedingungen zu schreiben, aber Sie können es lesbarer machen, indem Sie Bedingungen in eine Variable setzen und diese in Ihrer if-Anweisung verwenden. Ich denke, das elseif-Konstrukt ist besser.