Maison  >  Article  >  développement back-end  >  Méthodes de gestion des exceptions en PHP

Méthodes de gestion des exceptions en PHP

墨辰丷
墨辰丷original
2018-06-09 09:55:021201parcourir

Cet article présente principalement les méthodes de gestion des exceptions en PHP. Les amis intéressés peuvent s'y référer. J'espère qu'il sera utile à tout le monde.

Chaque nouvelle fonctionnalité ajoutée au runtime PHP crée un nombre aléatoire exponentiel, de cette manière les développeurs peuvent utiliser et même abuser de cette nouvelle fonctionnalité. Cependant, ce n’est que lorsque de bons et de mauvais cas d’utilisation sont apparus que les développeurs sont parvenus à un consensus. À mesure que ces nouveaux cas émergent, nous pouvons enfin discerner quelle est la meilleure ou la pire approche.

La gestion des exceptions n'est en effet en aucun cas une nouvelle fonctionnalité en PHP. Mais dans cet article, nous aborderons deux nouvelles fonctionnalités basées sur la gestion des exceptions dans PHP 5.3. Le premier concerne les exceptions imbriquées et le second est un ensemble de nouveaux types d'exceptions étendus par SPL (une extension principale du mécanisme d'exploitation PHP actuel). Pour ces deux nouvelles fonctionnalités, les bonnes pratiques sont disponibles dans ce livre et méritent votre étude détaillée.


Remarque : certaines de ces fonctionnalités existent déjà dans les versions PHP antérieures à 5.3, ou du moins peuvent être implémentées dans les versions antérieures à 5.3. Lorsque cet article mentionne PHP 5.3, ce n'est pas strictement un runtime PHP. Cela signifie plutôt que les bases de code et les projets adoptent PHP 5.3 comme version minimale, mais aussi pour toutes les meilleures pratiques qui émergent au cours de cette nouvelle étape de développement. Les tentatives "2.0" faites par des projets spécifiques comme. Zend Framework, Symfony, Doctrine et PEAR.

Contexte

PHP 5.2 uniquement Une classe d'exception Exception. Selon les standards de développement Zend Framework/PEAR, cette classe est la classe de base pour toutes les classes d'exception de votre bibliothèque. Si vous créez une bibliothèque nommée MyCompany, selon les standards Zend Framework / PEAR, tous les fichiers de code de la bibliothèque commenceront par MyCompany_. Si vous souhaitez créer votre propre classe de base d'exception pour la bibliothèque : MyCompany_Exception, utilisez cette classe pour hériter d'Exception, puis le composant (composant) hérite et lève cette classe d'exception. Par exemple, si vous disposez d'un composant MyCompany_Foo, vous pouvez créer une classe de base d'exception MyCompany_Foo_Exception à utiliser dans le composant. Ces exceptions peuvent être interceptées par du code qui intercepte MyCompany_Foo_Exception, MyCompany_Exception ou Exception. Pour les autres codes de la bibliothèque qui utilisent ce composant, il s'agit d'une exception à trois niveaux (ou plus, selon le nombre de sous-classes de MyCompany_Foo_Exception), et ils peuvent gérer ces exceptions comme bon leur semble.


En php5, la classe d'exception de base prend déjà en charge les fonctionnalités d'imbrication. Qu’est-ce que la nidification ? L'imbrication est la possibilité d'intercepter une exception spécifique ou un nouvel objet d'exception créé en référençant l'exception d'origine. Cela permettra à l'attribut de l'appelant d'être reflété sur les deux classes d'exceptions qui apparaissent dans la bibliothèque de surcharge des types les plus publics, et bien sûr sur la classe d'exceptions avec le comportement d'exception d'origine.

Pourquoi ces fonctionnalités sont-elles utiles ? Il est souvent plus efficace de lancer des exceptions de votre propre type en utilisant un autre code. Ce code peut être du code provenant d'une bibliothèque de code tierce qui fournit des fonctions plus adaptables encapsulées à l'aide du modèle d'adaptateur, ou du code simple qui utilise certaines extensions PHP pour lever des exceptions.


Par exemple, dans le composant Zend_Db, il utilise le modèle d'adaptateur pour encapsuler des extensions PHP spécifiques afin de créer une couche d'abstraction de base de données. Dans un adaptateur, Zend_Db encapsule PDO, et PDO lancera avec sa propre exception. PDOException, Zend_Db doit intercepter ces exceptions spécifiques à PDO et les renvoyer comme un type prévisible et connu de Zend_Db_Exception. Cela donne au développeur la garantie que Zend_Db lancera toujours une exception de type Zend_Db_Exception (et pourra donc être interceptée). peut également accéder à l'exception PDOException initialement lancée si nécessaire.

L'exemple suivant montre comment un adaptateur de base de données fictif peut implémenter des exceptions intégrées :

class MyCompany_Database
{
 /**
  * @var PDO object setup during construction
  */
 protected $_pdoResource = null;
  
 /**
  * @throws MyCompany_Database_Exception
  * @return int
  */
 public function executeQuery($sql)
 {
  try {
   $numRows = $this->_pdoResource->exec($sql);
  } catch (PDOException $e) {
   throw new MyCompany_Database_Exception('Query was unexecutable', null, $e);
  }
  return $numRows;
 }
 
}

Afin d'utiliser des exceptions intégrées, vous Vous devez appeler la méthode getPrevious() de l'exception capturée : les extensions PHP implémentées ont toutes des interfaces OO (orientées objet). Par conséquent, ces API ont tendance à générer des exceptions plutôt que de se terminer par des erreurs. Les extensions PHP pouvant générer des exceptions incluent PDO, DOM, Mysqli, Phar, Soap et SQLite.

// $sql and $connectionParameters assumed
try {
 $db = new MyCompany_Database('PDO', $connectionParams);
 $db->executeQuery($sql);
} catch (MyCompany_Database_Exception $e) {
 echo 'General Error: ' . $e->getMessage() . "\n";
 $pdoException = $e->getPrevious();
 echo 'PDO Specific error: ' . $pdoException->getMessage() . "\n";
}
Nouvelles fonctionnalités : nouveaux types d'exceptions de base

Dans le développement PHP 5.3, nous avons présenté de nouveaux types d'exceptions intéressants. Ces exceptions existaient dans PHP 5.2. Ils sont implémentés dans l'extension SPL et sont répertoriés dans le manuel (ici). Étant donné que ces nouveaux types d'exceptions font partie du noyau PHP et également de SPL, ils peuvent être utilisés par toute personne exécutant du code avec PHP 5.3 (et supérieur). Bien que cela ne semble pas si important lors de l'écriture du code de la couche application, l'utilisation de ces nouveaux types d'exceptions devient plus importante lorsque nous écrivons ou utilisons des bibliothèques de code


那么为什么新异常是普通类型?以前,开发者试图通过在异常消息提醒中放入更多的内容来赋予异常更多的含义。虽然这样做是可行的,但是它有几个缺点。一是你无法捕获基于消息的异常。这可是一个问题,如果你知道一组代码是同样的异常类型与不同的提示消息对应不同异常情况下,处理起来的难度将相当的大。例如,一个认证类,在对$auth->authenticate();;它抛出异常的相同类型的(假设是异常),但不同的消息对应两个具体的故障:产生故障原因是认证服务器不能达到但是相同的异常类型却提示失败的验证消息不同。在这种情况下(注意,使用异常可能不是处理认证响应最好的方式),这将需要用字符串来解析消息从而处理这两种不同的情况。

这个问题的解决办法显然是通过某种方式对异常进行编码,这样就可以在需要辨别如何对这种异常环境做出反应的时候能够更加容易的查询到。第一个反应库是使用异常基类的$code属性。另一个是通过创建可以被抛出且能描述自身行为的子类或者新的异常类。这两种方法具有相同的明显的缺点。两者都没有呈现出想这样的最好的例子。两者都不被认为是一个标准,因此每个试图复制这两种解决方案的项目都会有小的变化,这就迫使使用这需要回到文档以了解所创建的库中已经有的具体解决方案。现在通过使用SPL的新的类型方法,也称作php标准库;开发者就可以以同样的方式在他们的项目中,并且复用这些项目的新的最佳的方法已经出现。


第二个缺点是使用详细信息的做法使得理解这些异常情况对那些非英语或英语能力有限的开发者来说十分困难。这可能会使的开发者在试图理解异常信息的含义的过程十分的缓慢。许多开发者也会写关于异常的文章,因为还未出现一个统一的整合过的标准所要有同这些开发者数量相同的不同的版本来描述异常消息所描述的情况。

所以我如何去使用它们,就用这些让人无语的密密麻麻的细节描述?

现在在SPL中有总共13个新的异常类型。其中两个可被视为基类:逻辑异常和运行时异常;两种都继承php异常类。其余的方法在逻辑上可以被拆分为3组:动态调用组,逻辑组和运行时组。

动态调用组包含异常 BadFunctionCallException和BadMethodCallException,BadMethodCallException是BadFunctionCallException(LogicException的子类)的子类,这意味着这些异常可以被其直接类型(译者注:就是异常自身的类型,大家都知道异常有很多种)、LogicException,或者Exception抓到(译者注:就是catch)你应该在什么时候使用这些?通常,你应该在由一个无法处理的__call()方法产生的情况,或者回调无法不是一个有效的函数(简单说,当某些东西并非is_callable())时使用。

例如:
 

// OO variant
class Foo
{
 public function __call($method, $args)
 {
  switch ($method) {
   case 'doBar': /* ... */ break;
   default:
    throw new BadMethodCallException('Method ' . $method . ' is not callable by this object');
  }
 }
 
}
 
// procedural variant
function foo($bar, $baz) {
 $func = 'do' . $baz;
 if (!is_callable($func)) {
  throw new BadFunctionCallException('Function ' . $func . ' is not callable');
 }
}

一个直接的例子,在__call时call_user_func()。这组异常在开发各种API动态方法的调用、函数调用时非常有用,例如这是一个可以被SOAP和XML-RPC客户端/服务端能够发送和解释的请求。


第二组是逻辑(logic )组。这组由DomainException、InvalidArgumentException、LengthException、OutOfRangeException组成。这些异常也是LogicException的子类,当然也是PHP的Exception的子类。在有状态不定,或者错误的方法/函数的参数时使用这些异常。为了更好地理解这一点,我们先看看最后一组异常

最后一组是运行时(runtime )组。它由OutOfBoundsException、OverflowException、RangeException、UnderflowException、UnexpectedValueExceptio组成。这些异常也是RuntimeException的子类,当然也是PHP的Exception的子类。在“运行时”(runtime)的函数、方法发生异常时,这些异常(运行时组)会被调用


逻辑组和运行时组如何一起工作?如果你看看对象的剖析,通常是发生的是两者之一。首先,对象将跟踪并改变状态。这意味着对象通常是不做任何事情。它可能会传递结构给它,它可能会通过setter和getter设置一些东西(译者注:例如$this->foo='foo'),或者,它可能会引用其他对象。第二,当对象不跟踪或改变状态,这代表正在操作——做它该做的事。这是对象的运行时(runtime)。例如,在对象的一生中,它可能被创建,设置一些东西,那么它可能会被setFoo($foo),setBar($bar)。在这些时候,任何类型的LogicException应该被提高。此外,当对象内的方法被带参数调用时,例如$object->doSomething($someVariation);在前几行检查$someVariation变量时,可能抛出一个LogicException。完成检查$someVariation后,它继续做它该做的doSomething(),这时被认为是它的“运行时”(runtime),在这段代码中,可能抛出RuntimeExcpetions异常。


要理解得更好,我们来看看这个概念在代码中的运用:
 

class Foo
{
 protected $number = 0;
 protected $bar = null;
 
 public function __construct($options)
 {
  /** 本方法抛出LogicException异常 **/
 }
  
 public function setNumber($number)
 {
  /** 本方法抛出LogicException异常 **/
 }
  
 public function setBar(Bar $bar)
 {
  /** 本方法抛出LogicException异常 **/
 }
  
 public function doSomething($differentNumber)
 {
  if ($differentNumber != $expectedCondition) {
   /** 在这里,抛出LogicException异常 **/
  }
   
  /**
   * 在这里,本方法抛出RuntimeException异常
   */ 
 }
 
}

现在理解了这一概念,那么,对代码库的使用者来说,这是做什么的呢?使用者可以随时确定对象的异常状态,他们可以用异常的具体的类型来捕获(catch)异常,例如InvalidArgumentException或LengthException,至少也是LogicException。通过这种级别的精度调整,和类型的多样,他们可以用LogicException捕获最小的异常,但也可以通过实际的异常类型获得更好的理解。同样的概念也适用于运行时的异常,可以抛出更多的特定类型的异常,并且不论是特定或非特定类型的异常,都可以被捕获(catch)。它可以给使用者提供更详细的情况和精确度。

下面是一个关于SPL异常的表,您可能会有兴趣

类库代码中的最佳实践

PHP 5.3 带来了新的异常类型, 同时也带给我们新的最佳实践. 除了将某些特定的异常(如: InvalidArgumentException, RuntimeException)标准化外, 捕捉组件级的异常, 也很重要. 关于这方面, ZF2 wiki 和 PEAR2 wiki 上面有深入的探讨.

简而言之, 除了上面提到的各种最佳实践, 我们还应该用 Marker Interface 来创建一个组件级的异常基类. 通过创建组件级的 Marker Interface, 用在组件内部的异常既能继承 SPL 的异常类型, 也能在运行时被各种代码捕捉. 我们来看下列代码:
 

// usage of bracket syntax for brevity
namespace MyCompany\Component {
 
 interface Exception
 {}
 
 class UnexpectedValueException 
  extends \UnexpectedValueException 
  implements Exception
 {}
 
 class Component
 {
  public static function doSomething()
  {
   if ($somethingExceptionalHappens) {
    throw new UnexpectedValueException('Something bad happened');
   }
  }
 }
 
}

如果调用上面代码中的 MyCompany\Component\Component::doSomething() 函数, doSomething() 抛出的异常可以当作下列异常类型捕捉: PHP 的 Exception, SPL 的 UnexpectedValueException, SPL 的 RuntimeException, 该组件的MyCompany\Component\UnexpectedValueException, 或该组件的 MyCompany\Component\Exception. 这为捕捉你的类库组件中的异常提供了极大的便利. 此外, 通过分析异常的类型, 我们也能看出某个异常的含义.

总结:以上就是本篇文的全部内容,希望能对大家的学习有所帮助。

相关推荐:

使用Snoopy类解析html文件的方法

php针对数组的删除、转换、分组、排序

php针对文件操作及字符串加密的方法

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn