OOP development and hooks

Sally CJ raises a good point in their comment that it is probably better to have each class initialize its own actions and filters.

I’ve been working with the same boiler plate for some time now and created my own version of this boiler plate on GitHub.

In that version I have a Back\Hooks and a General\Hooks, both Singletons and getting passed the Loader. This is how the General\Hooks looks in one project

<?php

namespace ACME\General;

class Hooks
{
        protected static $instance;
        protected $loader;

        public static function getInstance($loader)
        {
                if (self::$instance === null) {
                        self::$instance = new self($loader);
                }
                return self::$instance;
        }

        protected function __construct($loader)
        {
                $this->loader = $loader;
        }

        public function run()
        {
                $this->addActions();
                $this->addFilters();
        }

        protected function addActions()
        {
                AdvancedCustomFields\Options::add($this->loader);
                $this->loader->addAction('init', Shortcode\News::class, 'init');
                $this->loader->addAction('init', Shortcode\Courses::class, 'init');
                $this->loader->addAction('wp_footer', WooCommerce\Foo::class, 'print_modal');
                // and many more
        }

        protected function addFilters()
        {
                $this->loader->addFilter('the_content', Bar\Baz::class, 'filter_the_content', 999);
                // and many more
        }
}

Now in the case of AdvancedCustomFields\Options I already pass the loader and do the initialization inside that class, doing that for all the other cases would probably be much cleaner.

General\Shortcode\News looks like this

<?php

namespace ACME\General\Shortcode;

class News
{
    public static function init()
    {
        add_shortcode('acme_news', [__CLASS__, 'render']);
    }

    public static function render($raw_atts = [], $content = null, $tag = '')
    {
        $raw_atts = array_change_key_case((array)$raw_atts, CASE_LOWER);
        $atts = shortcode_atts([
            'size' => 'large',
            'amount' => '4',
            'offset' => '0',
            'exclude' => '',
        ], $raw_atts, $tag);

        /* filter $atts */
        $atts['amount'] = intval($atts['amount']);
        $atts['offset'] = intval($atts['offset']);

        /* get result */
        $result="";
        // actual code, doing WP_Query, etc.
        // never directly outputted but only saved in $result

        return $result;
}