Illuminating WordPress

34.9% *

  • blog (d'uh)
  • content management
  • e-commerce
* as of October 1st 2019

How?

Extensive use of:

  • filters
  • actions (technically also filters)

What does that look like?


              add_action(
                'after_setup_theme', // action / filter name
                [$this, 'removeZoomFromGallery'], // callback
                99, // priority
                2 // accepted argument count
              );
            

What does that look like in real life?


              add_filter('wp_print_styles', [$this, 'dequeueUnusedAssets'], 99);
              add_filter('wp_enqueue_scripts', [$this, 'dequeueUnusedAssets'], 99);
              add_filter('after_setup_theme', [$this, 'removeZoomFromGallery'], 99);
              add_filter('init', [$this, 'addProductBrandPostType']);
              add_filter('add_meta_boxes_product_brand', [$this, 'addProductBrandMetaBoxes']);
              add_filter('save_post', [$this, 'saveProductBrandMetaBoxData']);
              add_filter('init', [$this, 'addProductFunctionPostType']);
              add_filter('add_meta_boxes_product_function', [$this, 'addProductFunctionMetaBoxes']);
              add_filter('save_post', [$this, 'saveProductFunctionMetaBoxData']);
              add_filter('woocommerce_template_single_price', [$this, 'showPriceNotice'], 10);
              add_filter('woocommerce_after_add_to_cart_quantity', [$this, 'showShippingMethod'], 35);
              add_filter('woocommerce_single_product_summary', [$this, 'showDivider'], 31);
              add_filter('woocommerce_single_product_summary', [$this, 'showReturnAndShippingInfo'], 40);
              add_filter('woocommerce_single_product_summary', [$this, 'showDivider'], 41);
              add_filter('woocommerce_single_product_summary', [$this, 'showPaymentGateways'], 45);
              add_filter('woocommerce_single_product_summary', [$this, 'showDivider'], 49);
              add_filter('woocommerce_single_product_summary', [$this, 'showInteractionButtons'], 50);
              add_filter('woocommerce_after_single_product_summary', [$this, 'showDivider'], 10);
              add_filter('woocommerce_after_single_product_summary', [$this, 'showDescription'], 11);
              add_filter('woocommerce_after_single_product_summary', [$this, 'showProductDetails'], 15);
              add_filter('woocommerce_after_single_product_summary', [$this, 'showOtherProductsFromSameBrand'], 53);
              add_filter('woocommerce_after_single_product_summary', [$this, 'showDivider'], 54);
              add_filter('woocommerce_after_single_product_summary', [$this, 'showFaqArea'], 55);
              add_filter('woocommerce_after_single_product_summary', [$this, 'showDivider'], 56);
              add_filter('woocommerce_after_single_product_summary', [$this, 'showNewsletterForm'], 60);
              add_filter('woocommerce_single_product_image_thumbnail_html', [$this, 'showBrandLogoAndFunctionSymbol'], 10, 2);

              add_filter('init', function() {
                remove_filter('woocommerce_single_product_summary', 'woocommerce_output_product_data_tabs', 35);
                remove_filter('woocommerce_after_single_product_summary', 'woocommerce_output_related_products', 20);
                remove_filter('woocommerce_single_product_summary', 'sf_product_share', 45);
                remove_filter('woocommerce_single_product_summary', 'woocommerce_template_single_meta', 40);
                add_filter('woocommerce_after_single_product_summary', 'woocommerce_output_related_products', 50);
              }, 99);
            

... and that's only one feature.

Downsides of

No templating engine


              <main id="main" class="site-main">
                <?php
                  if ( have_posts() ) {
                    // Load posts loop.
                    while ( have_posts() ) {
                      the_post();
                      get_template_part( 'template-parts/content/content' );
                    }
                    // Previous/next page navigation.
                    twentynineteen_the_posts_navigation();
                  } else {
                    // If no content, include the "No posts found" template.
                    get_template_part( 'template-parts/content/content', 'none' );
                  }
                ?>
              </main>
            

hardcoded file names
(more or less)

  • index.php
  • style.css
  • header.php
  • footer.php
  • 404.php
  • comments.php
  • ...

Getting blade on board

Add required composer packages ...


                {
                  // ...
                  "require": {
                    "illuminate/support": "^5",
                    "illuminate/view": "^5"
                  }
                  // ...
                }
              

... and assemble.

Register services


              $this->container = new \Illuminate\Container\Container;

              $this->container->singleton('files', function() {
                return new \Illuminate\Filesystem\Filesystem;
              });

              $this->container->singleton('events', function() {
                return new \Illuminate\Events\Dispatcher;
              });
            

              $this->container->singleton('blade.compiler', function() {
                return new \Illuminate\View\Compilers\BladeCompiler(
                  $this->container['files'], // Filesystem
                  $this->cachePath // Configured value
                );
              });
            

              $this->container->singleton('view.engine.resolver', function() {
                $resolver = new \Illuminate\View\Engines\EngineResolver;
                
                $resolver->register('php', function() {
                  return new \Illuminate\View\Engines\PhpEngine;
                });
                
                $resolver->register('blade', function() {
                  return new \Illuminate\View\Engines\CompilerEngine(
                    $this->container['blade.compiler'],
                    $this->container['files'] // Filesystem
                  );
                });
                
                return $resolver;
              });
            

              $this->container->singleton('view.finder', function() {
                return new FileViewFinder(
                  $this->container['files'], // Filesystem
                  $this->viewPaths // Configured value
                );
              });
            

              $this->factory = new Factory(
                $this->container['view.engine.resolver'],
                $this->container['view.finder'],
                $this->container['events']
              );

              $this->factory->setContainer($this->container);
            

Done (almost)

At least we can do this now:


              echo $this->factory
                ->make('my.view')
                ->render()
            

Okay, we have pretty views.

What now?

Problems:

  • Arbitrary names
  • No way to configure them
  • ... but there is a solution:
Hackerman

Simplified WordPress life cycle

Check URL

Determine kind of data

Determine template to load


              apply_filter(
                "{$type}_template",
                string $template,
                string $type,
                array $templates
              )
            

... to the rescue


              // Default supported types by WordPress:
              $templateTypes = [
                'index', '404', 'archive', 'author', 'category',
                'taxonomy', 'date', 'embed', 'home', 'frontpage',
                'privacypolicy', 'page', 'paged', 'search',
                'singular', 'attachment', 'single', 'tag',
              ];

              foreach($templateTypes as $templateType) {
                add_filter(
                  "{$templateType}_template",
                  fn($template, $type, $templates) =>
                    $this->handleTemplateRequest($template),
                  99,
                  3
                );
              }
            

              public function handleTemplateRequest($template) {
                $this->desiredTemplate = $template;

                // Here we can decide if we want to intercept or not.
                // Now we have full flexibility with naming, etc. Yay!
                if (
                  get_option('maintenance_mode', false) &&
                  !is_user_logged_in()
                ) {
                  $this->desiredTemplate = 'pages.maintenance';
                }

                return dirname(__DIR__) . '/index.php';
              }
            

Almost there!

the full index.php


              <?php

              // Make sure we have no direct contact
              !defined('ABSPATH') && die('Safety first!');

              // Render page
              echo \IlluminatedWordPressTheme\TemplatePage::render();
            

              namespace IlluminatedWordPressTheme;

              class TemplatePage {
                private $template;

                private function __construct($template) {
                  $this->template = $template;
                }

                public static function render() {
                  // The class that registered all the filters
                  $template = ViewController::getInstance()->getTemplate();

                  return (new static($template))->renderPage();
                }
              }
            

              public function renderPage() {
                $data = $this->getData();

                if (!View::make()->exists("templates.{$this->template}")) {
                  $this->template = 'index';
                }

                return View::view("templates.{$this->template}")
                  ->with($data)
                  ->render();
              }
            

We solved our problems and made our lives easier.

Sebastian Wasser

/sebwas

Attributions & sources:

WordPress share: [https://w3techs.com/technologies/details/cm-wordpress/all/all](https://w3techs.com/technologies/details/cm-wordpress/all/all)

Table lamp by Creative Stall from [the Noun Project](https://thenounproject.com/search/?q=desk%20lamp&i=981470). The original work has been modified.