hook_module_implements_alter (), um Hook-Implementierungen in separate Dateien zu legen - schlechte Praxis?

7

In einer Reihe von Modulen, die ich geschrieben habe, verwende ich hook_module_implements_alter (), um die * .module-Datei aufzuteilen und Hook-Implementierungen in separate Dateien zu verschieben.

Der Hauptgrund ist, dass ich lange Dateien leidenschaftlich hasse. (und ja, das macht mich unglücklich über viele der Dateien in Drupal 7 und Contrib)

Beispiele:

Ich weiß, dass einige Dinge zu beachten sind:

  1. Es funktioniert möglicherweise nicht für alle Hooks. ZB booten oder installieren oder Hooks, die nicht über module_implements () aufgerufen werden.
  2. Aufgrund der Funktionsweise von module_implements () ist es wichtig, die entsprechende Datei in die Implementierung von hook_module_implements_alter () aufzunehmen.

Jetzt hörte ich von einem langjährigen und bekannten Drupal-Entwickler, dass diese Praxis problematisch ist. https://drupal.org/node/2230319 (Themenstarter und Kommentare Nr. 5 und Nr. 11).

Bevor ich anfange, Funktionen zu verschieben, möchte ich von anderen hören, ob und warum dies sowohl technisch als auch ästhetisch schlecht ist.

EDIT: Ich denke, dies könnte in zwei Fragen aufgeteilt werden:

  1. Ist es gut oder schlecht, Ihre * .module-Datei aufzuteilen, damit Hook-Implementierungen an anderer Stelle ausgeführt werden?
  2. Ist hook_module_implements_alter () zuverlässig genug oder sollte ich es lieber include_once __DIR__ . '/MYMODULE.filename.inc'direkt in der * .module-Datei verwenden? Was könnte möglicherweise schief gehen, wenn Sie dazu hook_module_implements_alter () verwenden?
Donquijote
quelle
Ich möchte, dass andere die Trennung abwägen, aber ich denke, dass es gut sein kann. Ich bin wirklich im Warum von # 2.
mpdonadio
(Übrigens, einige der Antworten sind Dinge, die ich bereits kenne, aber das ist dennoch relevant und wichtig, um dies für andere Menschen nützlich zu machen. Und sie bestätigen, dass das Aufteilen eine gängige Praxis ist. Also bekommen beide +1 von mir. )
Donquijote

Antworten:

4

Ich habe gerade die fehlende Information gefunden.
EDIT: Und noch eine.

Problem mit module_invoke ()

module_invoke_all()ruft immer auf module_implements(), wodurch die Modul-Include-Datei überprüft wird, indem sowohl hook_hook_info()und als auch überprüft werden hook_module_implements_alter().

Allerdings module_invoke()nur Schecks hook_hook_info()und nicht hook_module_implements_alter() .

module_invoke()heißt zB in _block_render_blocks()für hook_block_view(). Dies bedeutet, dass dies hook_module_implements_alter()keine gute Lösung ist, um eine hook_block_view()Implementierung in eine separate Datei aufzuteilen .

Dies war ein Problem in Crumbs, siehe https://www.drupal.org/node/2328535 .

Bei anderen Hooks funktioniert es im Allgemeinen in Ordnung, aber Sie wissen nie, ob ein bestimmtes Modul module_invoke()anstelle von verwendet werden soll module_invoke_all().

Modul_implements () Cache im Bootstrap verschmutzt.

Ein weiteres Problem wird durch ein Kernproblem verursacht, das hier gemeldet wird:
module_implements_cache () kann durch Hook_boot () -Implementierungen verschmutzt werden, die module_invoke_all () direkt oder indirekt aufrufen

Dies gilt nur für Nicht-Boot-Module , bei denen es vorkommen kann, dass die Implementierung von hook_module_implements_alter()nie erkannt wird.

  1. Eine unerwartete Kette von Ereignissen führt dazu, module_implements($hook)dass von aufgerufen wird hook_boot().
    Dies $hookkann ein beliebiger zufälliger Hook sein, er muss nicht mit dem Modul verknüpft sein, an dem wir arbeiten.
  2. Dies führt module_implements('module_implements_alter')dazu, dass aufgerufen wird.
  3. Drupal sucht nach allen Implementierungen von hook_module_implements_alter(). Zu diesem Zeitpunkt MODULENAME.moduleist noch nicht enthalten.
    Für den Rest der Anforderung geht Drupal daher davon aus, dass die Implementierung MODULENAME_module_implements_alter()nicht vorhanden ist.
  4. Später in der Anforderung werden Implementierungen anderer Hooks erkannt, aber die Implementierungen von MODULENAME werden nicht gefunden, da sie MODULENAME_module_implements_alter()nicht ausgeführt werden.

Alternativen

einmalig benötigt

Alternativen wurden bereits erwähnt. Anstelle dieser Tricks kann man die Datei direkt einbinden. Es kann eine winzige Auswirkung auf die Leistung geben, aber dies wird wahrscheinlich von vielen anderen Dingen in Drupal überschattet.

Aber anstatt zu verwenden module_load_include(), würde ich eine direktere Lösung vorschlagen:

require_once __DIR__ . '/crumbs.block.inc';

Oder um PHP 5.2-kompatibel zu sein:

require_once dirname(__FILE__) . '/crumbs.block.inc';

Warum nicht module_load_include ()?

Es besteht im Allgemeinen keine Notwendigkeit zum Aufrufen module_load_include(), es kann sogar schädlich sein, wenn die * .module-Datei von you-dont-know-where (z. B. from settings.php) enthalten ist und Sie nicht wissen, ob sie module_load_include()verfügbar ist.

Diese Funktion dient hauptsächlich dazu, den Standort anderer Module zu bestimmen . Wenn Sie jedoch in dasselbe Modul aufnehmen , ist es im Allgemeinen sicherer und einfacher und schneller, mit relativen Dateipfaden und expliziten zu arbeiten require_once.

Rufen Sie die Hilfsfunktion auf

Wie von Jimajamma erwähnt, können Sie den Hook auch in der Hauptdatei implementieren *.module, dann eine andere Datei einschließen und von dort aus eine Hilfsfunktion aufrufen. Dies erfolgt zB in der Display Suite.

Diese Lösung ist in Ordnung, obwohl ich denke, dass sie die Hauptdatei immer noch überfüllt und Ihnen noch mehr Orte bietet, an denen Sie suchen können.

Und auch hier können Sie require_once anstelle von verwenden module_load_include(), wenn es sich innerhalb desselben Moduls befindet.

Donquijote
quelle
Danke für den nachdenklichen Zusammenbruch. Ich brauchte einen hilfreichen Schub, um ein halbes Dutzend verwandter, aber separater benutzerdefinierter Module für einen Kunden in einem zusammenzufassen. Ich denke, ich werde aufhören zu versuchen, es zu überdenken, und einfach requiredie zusätzlichen Dateien. Eine gute Namenskonvention für diese Dateien hilft auch, und dann wird das Ganze viel besser analysierbar.
Charlie Schliesser
Warum sollten Sie verschiedene Module zu einem kombinieren? Trennung ist gut! (hängt natürlich ab)
Donquixote
Oh Trennung von Bedenken ist ein Muss! Und Module, die abstrakt bleiben können (dh Module sein), sollten. Bei diesem speziellen Projekt habe ich anscheinend irgendwann gedacht, dass das gleiche Paradigma für die Geschäftslogik gilt, die sich innerhalb der App entfaltet und weiterentwickelt hat. Es ist nicht wirklich der gleiche Fall, und einige der Module sind nicht wirklich Module, sondern Sammlungen von PAC-Logik, die ohne einander keinen Sinn ergeben.
Charlie Schliesser
Warum benutzt du nicht hook_hook_info_alter()stattdessen? Treffen die gleichen Überlegungen / Probleme zu, wenn ich hook_hook_info_alter()die Hook-Implementierungen in eine separate Datei aufteile?
Елин Й.
hook_hook_info_alter () gilt für alle Implementierungen dieses Hooks. Somit hat es unbeabsichtigte Nebenwirkungen auf andere Module.
Donquijote
2

Jedes große Projekt, an dem ich gearbeitet habe, hat die Dinge in überschaubare und leicht zuzuordnende Teile zerlegt. Im Grunde genommen endet es wie hier erwähntes @tenken oder bis zu dem Punkt, an dem es foo.moduleam Ende nur noch Folgendes beinhaltet:

<?php

module_load_include('inc', 'foo', 'foo.defines');         // bring in our define()s
module_load_include('inc', 'foo', 'foo.HOOK1');           // bring in our HOOK1 functions
module_load_include('inc', 'foo', 'foo.HOOK2');           // bring in our HOOK2 functions

// ...etc

Natürlich habe ich auch Situationen gesehen und verwendet, in denen nur Administratoren oder auf andere Weise selten verwendete Hooks enthalten sind, wie:

function foo_HOOK(/* $args */) {
  module_load_include('inc', 'foo', 'foo.HOOK');
  return _foo_HOOK(/* $args */);  // _foo_hook() is defined in foo.HOOK.inc
}
Jimajamma
quelle
ja, das gesehen und selbst gemacht. beides. obwohl ich in letzter Zeit nur include_once __DIR__ bevorzuge. '/filename.inc'. Also ... ich denke, wir sind uns dann über Frage 1 klar, es ist alles andere als unorthodox, Dinge aufzuteilen. Bleibt Frage zwei, ob etwas Schlimmes passieren könnte, wenn Sie dafür hook_module_implements_alter () verwenden. Oder wie groß der Leistungsvorteil sein könnte, wenn die Datei nicht in eine durchschnittliche Anforderung aufgenommen wird, in der beispielsweise hook_menu () nicht aufgerufen wird.
Donquijote
Übrigens mag ich den Trick _foo_HOOK () nicht allzu sehr, er lässt mich an zwei Stellen statt an einer schauen.
donquixote
Übrigens können Sie zu module_load_include ('defined.inc', 'foo') vereinfachen. Aber was ist lrfm?
Donquijote
lol, lrfm war das Modul, aus dem ich das ausgeschnitten und eingefügt habe und das ich vergessen habe, zu foo zu wechseln :)
Jimajamma
Was Ihre Vereinfachung betrifft , ja, das würde sicherlich funktionieren, aber das erste Argument soll der Dateityp sein, und gemäß den Beispielen in api.drupal.org/api/drupal/includes%21module.inc/function/… , Ich bezweifle imho, was die "Drupalkräfte an" sein sollen, aber sicherlich weniger tippen.
Jimajamma
1

Wenn ich versuche, meine .module-Datei auf mehrere Dateien aufzuteilen, mache ich in der Regel das, was Features for D7 für die "enthaltenen" Dateien tut, für die include_once () verwendet wird:

include_once('MYMODULE.features.inc');

Für D7 verwende ich weiterhin den Namen meines Moduls "Funktionsnamensraum" für diese enthaltenen Dateien, und den Funktionen des privaten Moduls wird normalerweise das Präfix "_MYMODULE_foo ()" vorangestellt.

Diese Antwort ignoriert die gewünschte Verwendung von Autoloading-Klassen und alle OO-bezogenen Muster, die Sie in Ihrem Code verwenden möchten (Code-Abhängigkeiten usw.).

In Ihrer verknüpften Ausgabe möchten Sie hook_module_implements_alter () verwenden, um einen lesbaren Index dafür zu erhalten, welche Dateien welche Funktionen in Ihrer .module-Datei haben. Ich denke, ein vernünftiger Dateiname 'MYMODULE.PURPOSE.inc' reicht aus, um einen Entwickler auf die Verwendung dieser Include-Datei in Ihrem Modul hinzuweisen.

Tenken
quelle
Die Idee ist, die entsprechende Datei nur dann einzuschließen, wenn sie benötigt wird. Ich denke dort über Leistung nach, aber ich kenne die tatsächlichen Auswirkungen auf die Leistung nicht. Ich denke, mit APC oder ähnlichem macht es keinen großen Unterschied, aber ich habe es nie gemessen.
Donquijote
1
@donquixote Wenn Sie über Leistung nachdenken, installieren Sie APC oder einen anderen Opcode-Cache und kümmern Sie sich nicht so sehr um das Teilen.
Mołot