Uploaded image for project: 'Moodle'
  1. Moodle
  2. MDL-74954

Hooks as replacement for some one-to-many lib.php callbacks based on PSR-14

XMLWordPrintable

      Why hooks?

      In general lib.php callbacks used via plugin_callback() and get_plugins_with_function() have multiple known problems:

      • lack of documentation - list of callbacks is not maintained, the parameters are not documented properly
      • unnecessary memory use caused by inclusion of lib.php files
      • potential naming conflicts when adding new callbacks, this limits adding of callbacks in stable branches
      • hardcoded support for limited number of plugin types in some areas
      • poor backwards/forwards compatibility - callback function signature cannot be changed easily
      • plugin callbacks cannot be used during installation and upgrade which prevents their use in some low level APIs
      • it is hard to differentiate purpose of different lib.php functions - one-to-many or one-to-one callback; plugin APIs; local customisation callbacks, etc.

      Hooks were already proposed in MDL-44078 as a replacement for some lib.php callbacks, unfortunately they were not accepted. I'd like to ask HQ developers to reconsider inclusion of hooks into Moodle codebase. One of the reasons was the lack of standards, this proposal implements hooks using PSR-14.

      For more information about current one-to-many callbacks see https://docs.moodle.org/dev/Callbacks. Please note this proposal is not relevant to on-to-one callbacks that are also defined in lib.php files, also some one-to-many callbacks describing features of one particular plugin type are not be suitable candidates for conversion to hooks either.

      Hooks architecture

      Hooks are similar to standard events, the main difference is that hooks allow two-way communication between 2 or more different components.

      The architecture is based on PSR-14 recommendation.

      Hook class defines the data structure that is used for communication between plugins, hook instance can be modified by any registered callback. Hooks are mutable.

      Hook classes are usually stored in \any_plugin\hook\ namespace, hook callbacks are registered by adding db/hooks.php files. All hook classes should implement \core\hook\described_hook interface. Hooks that replace preexisting plugin callbacks should implement \core\hook\
      deprecated_callback_replacement interface.
      Admin page with hooks overview:

      Hook callback registration example:

      <?php
       
      // Contents of file admin/tool/recyclebin/db/hooks.php
       
      $callbacks = [
          [
              'hook' => core\hook\course_delete_pre::class,
              'callback' => tool_recyclebin\hook_callbacks::class . '::course_delete_pre',
          ],
      ]; 

      Migration from lib.php callbacks to hooks

      Each hook may contain a list of lib.php plugin callbacks that are deprecated by the hook.

      Functions plugin_callback() and get_plugins_with_function() may be asked to lookup if callback is deprecated and if the called component already implements the new hook callback and prints deprecation message or ignored the old plugin callback. The options are:

      1. lib.php plugin callback was not replaced by any hook - nothing changes and everything works as before for now
      2. lib.php plugin callback was marked as deprecated by some hook, the plugin with callback does not use the new hook yet - deprecation message is printed and old callback is called
      3. lib.php plugin callback was marked as deprecated by some hook, a plugin implements both the old lib.php callback and registered a new hook callback - new hook callback is called, old lib.php callback is ignored

      Benefits of new hooks

      1. Fully compliant with PSR-14.
      2. Any plugin type can register as callback for any hook.
      3. All available hooks in system are listed on new admin page (excluding unused hooks in non-standard locations).
      4. Optimal memory use when executing hooks due to class autoloading.
      5. Hook data can be altered while keeping backwards/forwards compatibility.
      6. Hooks may contain utility methods, not just data.
      7. Hooks can be used during installation and upgrades.
      8. Hooks can be used in PHPUnit internals - such as for static variables reset callbacks.
      9. Hooks may implement stoppable interface.
      10. In special cases sysadmins may alter callback order or disable some hook callbacks completely.

      Difference from events

      Events are triggered after something happens - hooks can be used to decide if something is allowed to happen, they can be used to alter how things happen, other plugins can be notified before something happens.

      All events are logged - hooks are not logged anywhere.

      Event data cannot be modified - hook data can be modified from callbacks returning data back to where the hook was dispatched.

      Events are not reliable during installation and upgrades - hooks can be used during installation and upgrade.

            skodak Petr Skoda
            skodak Petr Skoda
            Farhan Karmali Farhan Karmali
            Andrew Lyons Andrew Lyons
            CiBoT CiBoT
            Votes:
            5 Vote for this issue
            Watchers:
            25 Start watching this issue

              Created:
              Updated:
              Resolved:

                Estimated:
                Original Estimate - Not Specified
                Not Specified
                Remaining:
                Remaining Estimate - 0 minutes
                0m
                Logged:
                Time Spent - 3 days, 3 hours
                3d 3h

                  Error rendering 'clockify-timesheets-time-tracking-reports:timer-sidebar'. Please contact your Jira administrators.