PHP的__autoload()有多独特?

12

PHP的__autoload()(文档)对我来说非常有趣。其工作原理如下:

  • 您尝试使用类,比如new Toast_Mitten()(脚注1
  • 该类未加载到内存中。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()告诉我有一个特殊的烤面包类。好的,这里有一个实例!"

因此,在您的代码中可以开始使用通用手套,当您需要特殊行为的烤面包时,只需创建该类,然后砰!——您的代码就在使用它。

我的问题有两个方面:

  • (事实) 其他语言是否有类似的构造?我看到Ruby有一个autoload,但似乎必须在给定脚本中指定您希望在其上使用它的类。
  • (意见) 这太神奇了吗?如果您喜欢的语言没有这样做,您会认为:"嘿,真棒,我们应该有这个"还是"我很高兴X语言不那么松散?"

1向非英语母语的读者道歉。这是个小玩笑。据我所知,没有"烤面包手套"这样的东西。如果有的话,那将是一只用来拿热吐司的手套。也许在您自己的国家里有烤面包手套?


1
我喜欢使用这个,特别是在与经验较少的开发人员一起工作时...“为什么这个能工作,包括/需要在哪里”等等。 - Hannes
2
@Hannes - “它会让新手开发者感到困惑”不是不喜欢它的原因吗? - Nathan Long
1
@Nathan Long,我们随着挑战不断成长,我认为自己像是一个平凡的宫城先生。开玩笑的,这是一个很棒的概念,但如果你不熟悉它,它也可能会让你感到困惑。此外,如果你在这里应用自己的逻辑,应该尝试以有意义的方式使用它...例如class Category_Package_Subpackage_Class => category/package/subpackage/class.php - Hannes
我认为这只在无状态应用程序(如Web应用程序)中是必要的,这就是为仅在面向Web的语言中为您预制此功能的原因。 - dqhendricks
@Rolf "覆盖拇指和其他四个手指的手套分别包裹拇指和其他四个手指。" - Nathan Long
显示剩余3条评论
5个回答

5

Perl的AUTOLOAD有所不同。它允许您动态解释方法调用。 Perl的AUTOLOAD不会处理缺少的类。相比之下,PHP的__autoload加载类,但不会处理缺少的方法。 - Sean McMillan

3
  1. Do not use __autoload(). It's a global thing so, by definition, it's somewhat evil. Instead, use spl_autoload_register() to register yet another autoloader to your system. This allows you to use several autoloaders, what is pretty common practice.
  2. Respect existing conventions. Every part of namespaced class name is a directory, so new MyProject\IO\FileReader(); should be in MyProject/IO/FileReader.php file.
  3. Magic is evil!

    The Mitten::factory method can say to itself, "let's see, do I have a subclass called Toast_Mitten()? If so, I'll return that; if not, I'll just return a generic instance of myself - a standard mitten. Oh, look! __autoload() tells me there is a special class for toast. OK, here's an instance!"

    Rather such tricky code, use simple and verbose one:

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

好的观点,但我不一定同意你在第三点举的例子。我不能给出具体的例子,但我熟悉一个商业案例,在许多部门中标准手套是可以的,至少最初是这样,但他们中的任何一个或全部都可能最终想要一个定制手套。假设我们需要遍历所有部门并部署适当的手套。如果需要,创建一个新的“会计手套”类很好,并自动用于他们,而不是在工厂中有多个try/catch或case语句需要更新。 - Nathan Long

1

查看Ruby的Module#const_missing

我刚学到:Ruby的Module上有一个叫做const_missing的方法,如果你调用Foo::Bar而且Bar还没有被加载到内存中(虽然我想Foo必须已经在内存中了),那么这个方法就会被调用。

ruby-doc.org上的这个例子展示了如何使用它来实现该模块的自动加载器。根据Russ Olsen的《Eloquent Ruby》(第21章,“使用method_missing进行灵活的错误处理”,也涵盖了const_missing),这实际上是Rails用于加载新的ActiveRecord模型类的方法。

这是因为采用了“约定优于配置”的思想,所以如果你引用了一个名为ToastMitten的模型,如果它存在,它将在app/models/toast_mitten.rb中。如果你可以把这个模型放在任何地方,Rails 就不知道去哪里寻找它。即使你不使用 Rails,这个例子和我问题中的第一点都展示了遵循约定的实用性,即使是自己创建的约定。


示例的更新链接:http://ruby-doc.org/core/Module.html#method-i-const_missing - gaRex
为什么仅有的一种无需使用"require"和"autoload"就能实现真正的魔法自动加载的方法被称为不好呢? - gaRex
@gaRex - 我已按照你的建议更新了链接。那里的负面描述已被删除,因此我也在我的答案中将其删除了。 - Nathan Long

1

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


1

Java 也有类似的东西。它被称为 ClassLoader。其他编程语言可能也有,但它们会使用一些默认实现。

顺便说一下,如果 __autoload 能够加载任何类型的符号,而不仅仅是类:常量、函数和类,那就太好了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接