Problem/Motivation

PHP throws a fatal error if you include a file that extends a class or implements an interface, when the parent class/interface is not loaded.

If a class is provided by a contrib module, or core in some cases, it is not safe to put your classes in a .module file - better to use PSR-4 Class Loading.

Even if you have a dependency on a module, it's possible that both your module and the dependency are disabled when your .module file is included, and the registry won't autoload a class from a disabled module.

Also in hook_boot(), dependencies of modules aren't loaded, so if you add the class, then later implement hook_boot(), your module could be loaded without the dependency, and that will throw a fatal error too.

Proposed resolution

This needs to be documented somewhere, chx suggested the docs for .info.yml files.

Remaining tasks

Create Drupal.org documentation stating that classes cannot be reliably loaded in .module files and link to the proper method.

#1971198: [policy] Drupal and PSR-0/PSR-4 Class Loading
#1029606: Regression: Not loading the .module file causes a fatal error when uninstalling some modules (as does loading it)
#534594: [meta] system info cache writing and registry rebuilding is broken without a full bootstrap

Original report by catch

PHP throws a fatal error if you include a file that extends a class or implements an interface, when the parent class/interface is not loaded.

So if a class is provided by a contrib module, or core in some cases, it is not safe to put your classes in a .module file - better to use a .inc and use files[] in your .info.

Even if you have a dependency on a module, it's possible that both your module and the dependency are disabled when your .module file is included, and the registry won't autoload a class from a disabled module.

Also in hook_boot(), dependencies of modules aren't loaded, so if you add the class, then later implement hook_boot(), your module could be loaded without the dependency, and that will throw a fatal too.

This needs to be documented somewhere, chx suggested the docs for .info files.

Comments

fago’s picture

Even if you have a dependency on a module, it's possible that both your module and the dependency are disabled when your .module file is included, and the registry won't autoload a class from a disabled module.

Imo, this is developer WTF. If I depend on something, I'd expect it's there.

Since the problem has been fixed for hook_uninstall(), the only point it doesn't work is hook_boot() right? So, I'd recommend documenting this exception there, such that anyone implementing hook_boot() is aware of it.

catch’s picture

It's not fixed in hook_uninstall() if you load the .module file yourself.

fago’s picture

Of course, but then it's up to you.

We might want to point out that for uninstall and update hooks dependencies are generally not met, though.

chx’s picture

http://api.drupal.org/api/drupal/includes--common.inc/function/drupal_pa... i do not even see files[] mentioned and then the possibility of autoload and so on.

xjm’s picture

Issue tags: +Novice, +Needs backport to D7

This seems like something straightforward enough to document.

Edit: In D8 the picture is (I think?) a lot cleaner with the PSR-0 standard. Correct? Is this already a D7-only issue?

Berdir’s picture

It might still make sense to document this somewhere for D8. Even with PSR-0, you can still place a class in a file that is always included and PHP file find it. As D8 is for example still doing for the PagerDefault and Tablesort classes.

Not sure *where* this should be documented, though. For D8, I think the best place might be http://drupal.org/node/608152 ?

catch’s picture

Priority: Major » Normal

Going to downgrade this from major, still seems worth doing but it's not really urgent.

TransitionKojo’s picture

I've added the documentation to the "Use of Interfaces" section of the Object-oriented code documentation. It's the third paragraph. But, it might need a little more editing for clarity.

YesCT’s picture

@Transition I dont know if this improves the clarity of the paragraph you added... see what you think. I do recommend pulling out a really concise sentence that says "do it this way". Put that sentence before the paragraph you added. If I am understanding this, the paragraph you added gives detailed reasoning for including using the .info and explains two special cases for when errors will happen if that recommendation is not followed.

So.. maybe something like:

The use of a separate interface definition from an implementing class is strongly encouraged because it allows more flexibility in extending code later. A separate interface definition also neatly centralizes documentation making it easier to read. All interfaces should be fully documented according to established documentation standards.

If there is even a remote possibility of a class being swapped out for another implementation at some point in the future, split the method definitions off into a formal interface. A class that is intended to be extended must always provide an interface that other classes can implement rather than forcing them to extend the base class.

Use an .inc file and use files[] in the .info file to extend a class or implement an interface.

If you include a file that extends a class or implements an interface, PHP generates a fatal error if the parent class or interface is not loaded. So, if a class is provided by a contributed module, or core in some cases, it is not safe to put your classes in a .module file. It's better to use an .inc file and use files[] in your .info file. For example, even if you have a dependency on a module, it's possible that both your module and the dependency are disabled when your .module file is included. Because the registry won't auto-load a class from a disabled module, that could cause an error. Also, when hook_boot() is run, module dependencies aren't loaded. So, if you add a class, then later implement hook_boot(), your module could be loaded without the dependency, and that will also generate a fatal error. Using an .inc file and using files[] in your .info file is needed to avoid those errors.

TransitionKojo’s picture

@YesCT,
I think the changes from the 1st two paragraphs had already been included (I think we worked on that last week), but I made changes to the 3rd paragraph. Can you get some others to take a look at it, to see if it makes sense?

Thanks.

xjm’s picture

Status: Active » Needs review

The text to review is on http://drupal.org/node/608152 (no patch).

Berdir’s picture

Version: 8.x-dev » 7.x-dev

Hm. Most of that page is specific to 8.x, with namespaces and so on. And 8.x already mandates that classes must be PSR-0.

Wondering if we should indicate somewhere that that chapter is for 7.x. The about this page also incorrectly says Drupal 7 as the version, but I guess 8.x can't be selected yet. The differences between 7.x and 8.x are so big that it might make sense to have a completely separate page for 7.x?

donutdan4114’s picture

Version: 7.x-dev » 8.x-dev

Updated page version of Object-oriented documentation to 8.x.
@YesCT added a concise, bold sentence, "Use an .inc file and use files[] in the .info file to extend a class or implement an interface."

donutdan4114’s picture

Next steps: Should the Object Orientation page have both 7.x and 8.x techniques? Do we need separate pages, or just separate headings?

Possibly have "Class Loading" as a separate page altogether?

YesCT’s picture

Well, the one bolded sentence I suggest was added. :)

I agree I'm pretty confused looking at this issue.
Lets see what people think about #14

Then we can sort out what falls under 7.x and what under 8.x

YesCT’s picture

Issue summary: View changes

Formatted the summary description.

socketwench’s picture

Issue summary: View changes

Updated the issue summary to reflect use of PSR-4. Updated the reference to .info files to .info.yml files.

YesCT’s picture

Issue tags: -Novice
Mile23’s picture

Added documentation about declaring classes in your module file here: https://www.drupal.org/node/608152#declaring

Tempted to mark this issue fixed. :-)

Mile23’s picture

Status: Needs review » Fixed
Issue tags: -Needs backport to D7

OK, no one chimed in so it's fixed.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.