【问题标题】:PHP - Can I make a class that can't be instantiated except through another class?PHP - 我可以创建一个只能通过另一个类实例化的类吗?
【发布时间】:2012-07-07 23:09:57
【问题描述】:

我试图从 Exception 类中创建一个子类,只是为了处理错误并在给定错误代码的情况下发出正确的错误消息。我更改了原始代码并使其更简单,只是为了说明我的问题。

也许这是不可能的,但我不希望 InvalidEmailException 类被脚本实例化。如果有必要,我只希望订阅类使用它(发现错误)。我为什么要这样做呢?没关系,我只是想了解类的工作原理。

/* Child class from the parent Exception class to handle errors 
 * pertinent to users being subscribed
 */
class InvalidEmailException extends Exception{
   private $error_code;
   private $email;
   function __construct($error_code, $email){
      $this->error_code = $error_code;
      $this->email = $email;
      $this->notifyUser();
   }
   function notifyUser(){
        if($this->error_code == 2):
         echo "<p>Invalid email: <em>{$this->email}</em></p>";
      endif;
   }
}

// Initial class to subscribe a user with the try catch checks
class Subscribe{
   private $email;
   private $error_code = 0;
   function __construct($email){
      $this->email = $email;
      $this->validateEmail();
   }

   private function validateEmail(){
      try{
         if($this->email == ''):
            throw new Exception('<p>Error: empty email address.</p>');
         else:
            if($this->email == 'invalid test'){
                    $this->error_code = 2;
               throw new InvalidEmailException($this->error_code, $this->email);
            }elseif($this->error_code == 0){
               // Go to method to subscribe a user if the error code remains zero
               $this->subscribeUser();
            }
         endif;
      }catch(Exception $e){
         echo $e->getMessage();
      }
   }

   private function subscribeUser(){
      echo $this->email.' added to the database!';
   }
}

/* 
 * Script to use the Subscribe class, which would call 
 * the InvalidEmailException class if needed
 */
$email = 'invalid test'; // This could later on be used through the $_POST array to take an email from a form
$subscribe = new Subscribe($email); // Works well.
$test = new InvalidEmailException('2', 'a@b.c'); // Also works.  I want this to throw an error.

【问题讨论】:

  • 你如何加载类?有超载还是显式包含?
  • 我打算稍后使用自动加载功能。这只是一个例子。请原谅我的无知,但了解我如何加载类很重要?
  • 您尝试在编程中模拟友谊或组装事物(即某些类/功能仅可用于某些其他功能/类)。这可以在 PHP 中借助加载类的方式在一定程度上实现。
  • 我想我可能明白你在说什么。因此,如果我将 InvalidEmailException 和订阅类放在单独的文件中,并通过订阅类文件显式包含 InvalidEmailException 类,那么 InvalidEmailException 类将无法通过脚本实例化(如我来自问题的php代码)。请帮我解决这个问题,这是我能做到的唯一方法吗?
  • 如果你将它包含在类中,它可能会这样工作,是的,测试它

标签: php class exception exception-handling instantiation


【解决方案1】:

不,除了 PHP 中的“对所有事物都可见”之外,您实际上不能拥有任何类型的类可见性。您可以使用人们告诉您的各种技巧来部分近似它,但它们是毫无意义的浪费时间,因为它们无法强制执行可见性约束(如果程序员想要违反您的伪约束,所有这些都会受到微不足道的解决方法) ,如果没有强制执行,您也可以在程序员级别实现它(即,如果您认为不应该这样做,就不要这样做)。

如果您担心使用非常有限的异常类污染全局类命名空间(我会为您鼓掌),这就是namespaces 的用途;典型的专业 PHP 5.3 架构会将您的类设置为 YourApp\Email\SubscribeYourApp\Email\InvalidAddressException。 (顺便说一句,Subscribe 中的代码可以抛出 new InvalidAddressException,而不需要 use 语句或完整的命名空间规范。)

【讨论】:

  • 非常感谢。我需要确定 PHP 中不存在此类可见性,除了“对所有事物都可见”哈哈哈,谢谢。
【解决方案2】:

我认为你最好的选择是使用依赖注入http://en.wikipedia.org/wiki/Dependency_injection。它有点先进,但我认为从长远来看它会有所帮助..

本质上,您可以使用获取所需数据的方法创建一个通用接口,并使异常类的构造函数具有接口类型的参数。然后让订阅类实现接口,这将允许将其传递给异常的构造函数。

您也可以直接将订阅对象作为参数传递给异常类的构造函数,并绕过接口使用。但是,该接口允许您让多个类都使用该接口和那些常用方法,如果您使用该模式,它们都可以构造您的异常类。而且您还会知道如何与这些类进行交互,因为它们使用相同的通用设计。

这并不能完全阻止您从类外部和脚本中构造异常,但是您需要先构造实现接口的类(或仅构造类本身,具体取决于您采用的方式),这是限制行为的好方法。

【讨论】:

  • 哇,我只是个新手。我不确定我是否理解你写的任何东西,我读了两遍。但是,我以前听说过接口..它应该是最高级别?那么抽象类排在第二位?你能再用更简单的方式解释一下吗?而且..几乎我的问题中的php代码无法修改以使其工作?我必须彻底改变它(剧烈地,戏剧性地)......对吗?
  • @Tolowe 为您提供了一种出色的方法。您可能还想研究工厂模式:)
  • 接口本质上就像一个类必须实现某些方法的要求。在这种情况下,您可以使用 GetEmail 和 GetErrorCode 方法创建接口。如果您创建了一个接口 IException,然后让您的订阅类实现它,您将需要在那里定义方法。它在实例化时也像泛型类型一样起作用。例如,您可以执行 IException sub = new Subscribe(); 之类的操作。因此,在您的 EmailException 类的构造函数中,您可以传递参数(IException sub),并使用接口中定义的常用方法设置值。
【解决方案3】:

这是一个想法。

使用define 设置一个“全球化”变量。

因此,在您的订阅类 __construct 函数中设置一个变量,如 define( 'IN_USE', 1 );,然后如果/当调用 InvalidEmailException 时,只需检查:

if( !IN_USE ) { exit; }

【讨论】:

  • 嘿,这是个好主意。这也是一种廉价的解决方法,但至少我仍然可以使用自动加载功能根据脚本中的请求加载我的所有类。到目前为止,我认为这是我对这个问题得到的最理性的答案。非常感谢!
  • 我不知道发生了什么,我试图投票,因为我认为这很有用,它给了我一个信息,我需要 15 声望才能投票,然后它最终给了你对这个答案投了反对票……很奇怪。
  • @JuanVeliz:将此答案标记为已接受通常意味着这是您最终使用的满足您需求的解决方案。是这个意思吗?
  • 这是一个实用的解决方案,但现在我想这是我见过的最好的解决方案,无需更改我的代码。虽然我想要一种更好的方法来执行 InvalidException,以便只有订阅类可以实例化它。
猜你喜欢
  • 2010-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-04
  • 1970-01-01
  • 2018-07-10
相关资源
最近更新 更多