16

I had this question in mind along all the last week: When I should create a service or an utility function?

In the Drupal Core we have both Services and Utility functions, but I can't find the distinction between them (when I need to create a service or when I need to create an utility function).

I will take as example the Modules Weight module where I have the InternalFunctions class.

<?php

namespace Drupal\modules_weight\Utility;

class InternalFunctions {

  public static function prepareDelta($weight) {
    $delta = 100;

    $weight = (int) $weight;

    if ($weight > $delta) {
      return $weight;
    }

    if ($weight < -100) {
      return $weight * -1;
    }

    return $delta;
  }


  public static function modulesList($force = FALSE) {
    $modules = [];
    $installed_modules = system_get_info('module');

    $config_factory = \Drupal::service('config.factory');

    if ($force) {
      $show_system_modules = TRUE;
    }
    else {
modules.
      $show_system_modules = $config_factory->get('modules_weight.settings')->get('show_system_modules');
    }

    $modules_weight = $config_factory->get('core.extension')->get('module');

    foreach ($installed_modules as $filename => $module_info) {
      if (!isset($module_info['hidden']) && ($show_system_modules || $module_info['package'] != 'Core')) {
        $modules[$filename]['name'] = $module_info['name'];
        $modules[$filename]['description'] = $module_info['description'];
        $modules[$filename]['weight'] = $modules_weight[$filename];
        $modules[$filename]['package'] = $module_info['package'];
      }
    }
    uasort($modules, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);

    return $modules;
  }

}

In this class I have two static functions but they are both utility functions or prepareDelta() is an utility function and modulesList() should be in another class and have a service?

The only difference that I found at this time is that inside the namespace Drupal\Component\Utility (where you will see a lot of utility fucntions) none of them use inside a service and usually a service use another services inside (I don't have review all the services to validate this).

So, when I should create a service or an utility function?

6
  • Ken Rickard from Slack Drupal #contribute channel says: "I would create a service if you expect other modules (or other developers) to interact with that code. Utility methods are just private shortcuts for yourself." Commented Nov 28, 2017 at 18:52
  • That's what I was thinking about Utility methods, but for services sometimes I think that is more a consideration. Commented Nov 28, 2017 at 18:54
  • I think a lot of it comes down to what the class does, and what it needs to have made available to it in order to operate. Take the Unicode class in core - that's a static utility class, not a service, because it doesn't have any dependencies, and doesn't need to maintain any state. If it required a service dependency, the DI pattern would require it to be converted to a service, and you'd use the singleton (or factory-generated) instance from the container when you needed it. Otherwise you can just use the static class it when it makes sense.
    – Clive
    Commented Nov 28, 2017 at 20:07
  • 1
    @Clive it doesn't really need to be. One could argue it's the "Drupal way". Also, making it a service makes it easier for other devs to find what you can overwrite.
    – No Sssweat
    Commented Nov 28, 2017 at 20:16
  • 3
    @NoSssweat But Unicode is a Drupal class that contains only static methods! The fact that the core devs chose to implement it as a static class, rather than a service, probably means something don't you think? A utility class doesn't really need to be overwritten, by its nature - it does some things, if those things aren't what you want, you write your own class instead. Remember the sort of things that traditionally live in utility classes are one-shot, "I do this and nothing else" types of methods, that don't need input except a set of parameters
    – Clive
    Commented Nov 28, 2017 at 20:18

2 Answers 2

8

In general use services. See the following blog post when it is OK to use static utility functions:

So never use static?

Well no, there are valid use cases. One is that if you have a list of pre defined items static can help reduce memory since it will be on class level and not in any instances.

Other cases are utility methods which do not require outside dependencies, for instance a slugify method.

<?php
class Util
{
    public static function slug($string)
    {
        return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '_', $string)));
    }
}

The slug method only does a very well defined behavior. It is easy to take the behavior into account in unit tests and I would not be too worried when I see this call.

These methods can even be unit tested since they do not require initialization.

Source: https://stovepipe.systems/post/avoiding-static-in-your-code

(The amount of static code now in Drupal is because of the transition from procedural D7 code, so don't use Drupal in the current state as example.)


About the example from the question, the rest of the utility class (not shown in the question)

<?php

namespace Drupal\modules_weight\Utility;

/**
 * Provides module internal helper methods.
 *
 * @ingroup utility
 */
class InternalFunctions {

...

  /**
   * Return the modules list ordered by the modules weight.
   *
   * @param bool $force
   *   Force to show the core modules.
   *
   * @return array
   *   The modules list.
   */
  public static function modulesList($force = FALSE) {
    // If we don't force we need to check the configuration variable.
    if (!$force) {
      // Getting the config to know if we should show or not the core modules.
      $force = \Drupal::service('config.factory')->get('modules_weight.settings')->get('show_system_modules');
    }
    // Getting the modules list.
    $modules = \Drupal::service('modules_weight')->getModulesList($force);

    return $modules;
  }

}

calls the module own service in a static wrapper:

\Drupal::service('modules_weight')

This is probably because the utility class is used in legacy procedural code. In OOP code this is not necessary, here you should inject the service directly.

7
  • Thanks for the answer, yesterday I changed a little the module code (because I made some commits) and I create the modules_weight service. I have the service because this can be used by others modules and is now general, you can get all the modules or only the core modules list. But in the module this list can be affected by the value inside the show_system_modules config variable, so I made another function that takes this var and then call the services, but reading your answer it seems that the modulesList function should not be static. Commented Nov 29, 2017 at 15:14
  • In this case do you thing that the modulesList function should be inside the service or on another class with a constructor with the dependency injection? Commented Nov 29, 2017 at 15:15
  • I think you you can put it in the same service and declare getModulesList() as a protected method.
    – 4uk4
    Commented Nov 29, 2017 at 15:52
  • but the point is that if somebody wants to use getModuleList() will not be possible and modulesList() have access to a variable that only is important for the module. Maybe adding modulesList() as another method and adding in the description that use a module config variable? Commented Nov 29, 2017 at 16:02
  • I would only make one of the two methods public. Perhaps you can set a default value $force = NULL, so that you know if someone wants to override the config value with a FALSE.
    – 4uk4
    Commented Nov 29, 2017 at 16:17
11

Ken Rickard from Slack Drupal #contribute channel says: "I would create a service if you expect other modules (or other developers) to interact with that code. Utility methods are just private shortcuts for yourself."

Yes, a cool thing about services is that anyone can overwrite them. So if you want to give other people the ability to customise a particular piece of code. See Altering existing services, providing dynamic services.

In addition, you should make it a service if you need to do Mock test for PHP Unit testing. See Services and dependency injection in Drupal 8, see Unit testing more complicated Drupal classes.

Q & A:

Service unit test

Writing unit tests for a method that calls static methods from another class

8
  • you should create a service if it's something you see yourself using again in different files of your module Why would a service be more useful (or better practice) than having a utility class which is used several times in the same module? (to clarify: I'm not arguing, but there doesn't seem to be any difference in that context specifically. I'd love to hear why you think a service makes more sense)
    – Clive
    Commented Nov 28, 2017 at 19:41
  • @Clive you got me there on that context alone.
    – No Sssweat
    Commented Nov 28, 2017 at 20:12
  • @Clive can you post an answer with your opinions and some references? Commented Nov 28, 2017 at 20:15
  • 1
    Yeah it's an interesting one @NoSssweat. IMO it's guided by higher-level principles than Drupal or Symfony. I think you apply good, standard, class design to your code, and then slot the results into whatever framework you're using at the time by whatever method makes sense for that class
    – Clive
    Commented Nov 28, 2017 at 20:22
  • @AdrianCidAlmaguer I did start writing an answer, then about six paragraphs in, I realised to get down what I actually want to say about it would end up being rather a long essay. It's a very big topic :) If I think of a way to write it down succinctly I will do
    – Clive
    Commented Nov 28, 2017 at 20:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.