【问题标题】:How unique is PHP's __autoload()?PHP 的 __autoload() 有多独特?
【发布时间】:2011-04-27 09:15:56
【问题描述】:

PHP 的__autoload() (documentation) 对我来说非常有趣。以下是它的工作原理:

  • 你尝试使用一个类,比如new Toast_Mitten()(footnote1)
  • 该类尚未加载到内存中。 PHP 缩回拳头,用一个错误来敲你。
  • 它暂停了。 “等等,”它说。 “定义了一个__autoload() 函数。”它运行它。
  • 在该函数中,您以某种方式将字符串Toast_Mitten 映射到classes/toast_mitten.php 并告诉它需要该文件。确实如此。
  • 现在该类在内存中,您的程序继续运行。

内存优势:您只加载您需要的类。简洁的好处:您可以停止在任何地方包含这么多文件,而只需包含您的自动加载器。

如果

,事情会变得特别有趣

1) 您的__autoload() 有一种从类名中自动确定文件路径和名称的方法。例如,也许您所有的课程都在classes/ 中,而Toast_Mitten 将在classes/toast_mitten.php 中。或者,也许您将类命名为 Animal_Mammal_Weasel,它将在 classes/animal/mammal/animal_mammal_weasel.php 中。

2) 您使用工厂方法来获取您的类的实例。

$Mitten = Mitten::factory('toast');

Mitten::factory 方法可以对自己说:“让我们看看,我是否有一个名为 Toast_Mitten() 的子类?如果有,我将返回它;如果没有,我将返回我自己的一个通用实例- 一个标准手套。哦,看!__autoload() 告诉我一个特殊的 toast 类。好的,这是一个实例!"

因此,您可以开始在整个代码中使用通用手套,当您需要为 toast 提供特殊行为的那一天到来时,您只需创建该类并 bam! - 您的代码正在使用它。

我的问题有两个:

  • 事实)其他语言有类似的结构吗?我看到了Ruby has an autoload,但您似乎必须在给定的脚本中指定您希望在哪些类上使用它。
  • (意见) 这也太神奇了吧?如果您最喜欢的语言没有做到这一点,您会认为“嘿,漂亮,我们应该有那个”或“我很高兴语言 X 不是那么马虎?”

1 我向非英语母语人士道歉。这是一个小笑话。据我所知,没有“烤面包手套”这样的东西。如果有,那将是拿起热吐司的手套。也许您在自己的国家/地区有敬酒手套?

【问题讨论】:

  • 喜欢使用它,尤其是在与经验不足的开发人员一起工作时......“为什么它有效,包含/要求在哪里”等。
  • @Hannes - “它让新手开发者感到困惑”难道不是喜欢它的原因吗?
  • 在找到关于小玩笑的评论之前,我已经仔细阅读了您对自动加载的描述。在那之前,我一直在阅读 Toast_Kitten 而不是 Toast_Mitten……在我脑海中留下了不同的形象。就我个人而言,我发现自动加载非常有用,尤其是在需要内存的应用程序中,但是如果你有多个第三方库,每个库都使用自己的自动加载器(开发人员并不总是记得在这个案例,然后抱怨库不起作用)请参阅 Gordon 的解决方案评论
  • @Nathan Long 我们在挑战中成长,我认为自己是一个卑鄙的宫城先生,不开玩笑,这是一个很棒的概念,但如果你不熟悉它也会让你很困惑.此外,如果您在这里应用自己的逻辑,应该尝试以一种有意义的方式使用它......例如class Category_Package_Subpackage_Class => category/package/subpackage/class.php
  • @Hannes - 是的,当我第一次看到它时确实让我感到困惑,但幸运的是有一个像你展示的逻辑模式,所以一旦解释了,就很好了。

标签: php ruby language-agnostic language-features autoload


【解决方案1】:

Ruby 和 PHP 都从 Perl 中的 AUTOLOAD 获取它。

请注意,AutoLoader 模块是一组使用 AUTOLOAD 功能的常见任务的助手。

【讨论】:

  • :-/ Perl 的 AUTOLOAD 做了一些不同的事情。它允许您动态解释方法调用。 Perl 的 AUTOLOAD 不会处理丢失的类。相比之下,PHP 的 __autoload 会加载类,但不会处理丢失的方法。
【解决方案2】:
  1. 不要使用__autoload()。这是一个全局的东西,所以,根据定义,它有点邪恶。相反,使用spl_autoload_register() 将另一个自动加载器注册到您的系统。这允许您使用多个自动加载器,这是很常见的做法。
  2. 尊重现有惯例。 namespaced 类名的每一部分都是一个目录,所以new MyProject\IO\FileReader(); 应该在MyProject/IO/FileReader.php 文件中。
  3. 魔法是邪恶的!

    Mitten::factory 方法可以对自己说:“让我们看看,我是否有一个名为 Toast_Mitten() 的子类?如果有,我将返回它;如果没有,我将返回我自己的通用实例- 一个标准的手套。哦,看!__autoload() 告诉我有一个用于 toast 的特殊类。好的,这是一个实例!"

    相当棘手的代码,使用简单而冗长的代码:

    try {
        $mitten = new ToastMitten();
        // or $mitten = Mitten::factory('toast');
    } catch (ClassNotFoundException $cnfe) {
        $mitten = new BaseMitten();
    }
    

【讨论】:

  • 好点,但我不一定同意您在#3 中的示例。我无法举出具体的例子,但我熟悉一个商业案例,可以这么说,标准手套对许多部门来说都很好,至少最初是这样,但其中任何一个或所有部门最终都可能需要定制手套。假设我们需要遍历所有部门并部署适当的手套。很高兴能够在必要时创建一个新的 Accounting_Mitten 类,并将其自动用于它们,而不是在工厂中有多个 try/catch 或 case 语句需要更新。
【解决方案3】:

我认为这个功能非常方便,而且我还没有在其他地方看到过类似的功能。我在其他地方也不需要这些功能。

【讨论】:

    【解决方案4】:

    Java 也有类似的东西。它被称为ClassLoader。可能还有其他语言,但它们坚持一些默认实现。

    而且,当我们在这里的时候。如果__autoload 加载任何类型的符号,而不仅仅是类:常量、函数和类,那就太好了。

    【讨论】:

      【解决方案5】:

      查看 Ruby 的模块#const_missing

      我刚刚了解到:Ruby 在 Module 上有一个名为 const_missing 的方法,如果您调用 Foo::Bar 并且 Bar 尚未在内存中(尽管我认为 Foo 必须在内存中,则该方法会被调用) )。

      This example in ruby-doc.org 展示了一种使用它为该模块实现自动加载器的方法。这实际上是 Rails 用来加载新的 ActiveRecord 模型类的方法,根据 Russ Olsen 的“Eloquent Ruby”(第 21 章,“使用 method_missing 进行灵活的错误处理”,还涵盖了const_missing)。

      由于“约定优于配置”的心态,它能够做到这一点:如果您引用一个名为 ToastMitten 的模型,如果它存在,它将在 app/models/toast_mitten.rb 中。如果您可以将该模型放在您想要的任何地方,Rails 将不知道在哪里寻找它。即使您没有使用 Rails,这个示例和我的问题中的第 1 点也显示了遵循约定是多么有用,即使您自己创建它们也是如此。

      【讨论】:

      • 如果它只是实现 真正的魔法 自动加载而不需要和自动加载的单一可能方法,为什么它会被称为错误?
      • @gaRex - 按照您的建议更新了链接。那里的负面描述已被删除,因此我也将其删除。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-21
      • 2014-01-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多