felixarntz/theme-boilerplate

View on GitHub
inc/library/image-sizes/class-image-sizes.php

Summary

Maintainability
C
7 hrs
Test Coverage
<?php
/**
 * Super_Awesome_Theme_Image_Sizes class
 *
 * @package Super_Awesome_Theme
 * @license GPL-2.0-or-later
 * @link    https://super-awesome-author.org/themes/super-awesome-theme/
 */

/**
 * Class managing the theme's image sizes.
 *
 * @since 1.0.0
 */
final class Super_Awesome_Theme_Image_Sizes extends Super_Awesome_Theme_Theme_Component_Base {

    /**
     * Selectable image sizes as `$slug => $label` pairs.
     *
     * @since 1.0.0
     * @var array
     */
    protected $selectable_sizes = array();

    /**
     * Constructor.
     *
     * Sets the required dependencies.
     *
     * @since 1.0.0
     */
    public function __construct() {
        $this->require_dependency_class( 'Super_Awesome_Theme_Settings' );
        $this->require_dependency_class( 'Super_Awesome_Theme_Sidebar' );
    }

    /**
     * Registers an image size.
     *
     * @since 1.0.0
     *
     * @param string      $slug       Image size identifier.
     * @param int         $width      Image width in pixels.
     * @param int         $height     Image height in pixels.
     * @param bool        $crop       Optional. Whether to crop to the specified width. Default false.
     * @param bool|string $selectable Optional. Whether to make the size available for selection when
     *                                inserting an image into the editor. Passing a string will use that
     *                                value as label for the image size (recommended). Default false.
     */
    public function add_size( $slug, $width, $height, $crop = false, $selectable = false ) {
        add_image_size( $slug, $width, $height, $crop );

        if ( $selectable ) {
            $this->selectable_sizes[ $slug ] = is_string( $selectable ) ? $selectable : $slug;
        }
    }

    /**
     * Checks if an image size exists.
     *
     * Only considers custom image sizes, not the WordPress built-in ones.
     *
     * @since 1.0.0
     *
     * @param string $slug Image size identifier.
     * @return bool True if the image size exists, false if not.
     */
    public function has_size( $slug ) {
        return has_image_size( $slug );
    }

    /**
     * Removes an image size.
     *
     * @since 1.0.0
     *
     * @param string $slug Image size identifier.
     */
    public function remove_size( $slug ) {
        if ( isset( $this->selectable_sizes[ $slug ] ) ) {
            unset( $this->selectable_sizes[ $slug ] );
        }

        remove_image_size( $slug );
    }

    /**
     * Magic call method.
     *
     * @since 1.0.0
     *
     * @param string $method Method name.
     * @param array  $args   Method arguments.
     *
     * @throws BadMethodCallException Thrown when method name is invalid.
     */
    public function __call( $method, $args ) {
        switch ( $method ) {
            case 'register_sizes':
            case 'filter_selectable_sizes':
            case 'calculate_content_image_sizes':
                return call_user_func_array( array( $this, $method ), $args );
            default:
                /* translators: %s: method name */
                throw new BadMethodCallException( sprintf( __( 'Call to undefined method %s', 'super-awesome-theme' ), __CLASS__ . '::' . $method . '()' ) );
        }
    }

    /**
     * Registers the image sizes used by the theme.
     *
     * @since 1.0.0
     */
    protected function register_sizes() {
        $rem = 16;

        // The maximum site width is 72rem.
        $this->add_size( 'site-width', 72 * $rem, 9999, false, __( 'Site Width', 'super-awesome-theme' ) );

        // The maximum content width is 40rem.
        $this->add_size( 'content-width', 40 * $rem, 9999, false, __( 'Content Width', 'super-awesome-theme' ) );

        // Featured images span the maximum content width and should be in 16:9 aspect ratio.
        set_post_thumbnail_size( 40 * $rem, ( ( 40 * $rem ) / 16 ) * 9, true );
    }

    /**
     * Filters the selectable image sizes, including custom selectable sizes.
     *
     * @since 1.0.0
     *
     * @param array $sizes Image sizes as `$slug => $label` pairs.
     * @return array Filtered image sizes.
     */
    protected function filter_selectable_sizes( array $sizes ) {
        return array_merge( $sizes, $this->selectable_sizes );
    }

    /**
     * Filters the sizes attribute generated for an image.
     *
     * @since 1.0.0
     *
     * @param string $sizes The sizes attribute to filter.
     * @param array  $size  Array with width and height.
     * @return string Filtered sizes attribute.
     */
    protected function calculate_content_image_sizes( $sizes, $size ) {
        $settings = $this->get_dependency( 'settings' );
        $sidebar  = $this->get_dependency( 'sidebar' );

        $rem         = 16;
        $image_width = $size[0];

        if ( $sidebar->should_display_sidebar() ) {
            $max_width      = 72 * $rem;
            $double_padding = 2 * $rem;
            $breakpoint     = 48 * $rem;

            $sidebar_size = $settings->get( 'sidebar_size' );
            switch ( $sidebar_size ) {
                case 'large':
                    $max_content_width    = 36 * $rem;
                    $max_content_width_vw = 50;
                    break;
                case 'small':
                    $max_content_width    = 54 * $rem;
                    $max_content_width_vw = 75;
                    break;
                default:
                    $max_content_width    = 48 * $rem;
                    $max_content_width_vw = 66.666666;
            }

            $max_content_width_minus_padding  = $max_content_width - $double_padding;

            if ( $image_width > $breakpoint && $image_width > $max_content_width ) {
                $sizes = "(max-width: {$breakpoint}px) calc(100vw - {$double_padding}px), (max-width: {$max_width}px) calc({$max_content_width_vw}vw - {$double_padding}px), {$max_content_width_minus_padding}px";
            } else {
                $sizes = "(max-width: {$breakpoint}px) calc(100vw - {$double_padding}px), {$image_width}px";
            }
        } else {
            // WordPress doesn't expose the context, so we assume 'alignwide' as best bet.
            $context = 'alignwide';

            switch ( $context ) {
                case 'alignwide':
                    $max_width      = 72 * $rem;
                    $double_padding = 2 * $rem;
                    break;
                case 'alignfull':
                    $max_width      = 0;
                    $double_padding = 0;
                    break;
                default:
                    $max_width      = 40 * $rem;
                    $double_padding = 2 * $rem;
            }

            $max_width_minus_padding  = $max_width - $double_padding;
            $image_width_plus_padding = $image_width + $double_padding;

            if ( $double_padding > 0 ) {
                if ( $max_width > 0 && $image_width > $max_width ) {
                    $sizes = "(max-width: {$max_width}px) calc(100vw - {$double_padding}px), {$max_width_minus_padding}px";
                } else {
                    $sizes = "(max-width: {$image_width_plus_padding}px) calc(100vw - {$double_padding}px), {$image_width}px";
                }
            } else {
                if ( $max_width > 0 && $image_width > $max_width ) {
                    $sizes = "(max-width: {$max_width}px) 100vw, {$max_width}px";
                } else {
                    $sizes = "(max-width: {$image_width}px) 100vw, {$image_width}px";
                }
            }
        }

        return $sizes;
    }

    /**
     * Adds hooks and runs other processes required to initialize the component.
     *
     * @since 1.0.0
     */
    protected function run_initialization() {
        add_action( 'after_setup_theme', array( $this, 'register_sizes' ), 10, 0 );
        add_filter( 'image_size_names_choose', array( $this, 'filter_selectable_sizes' ), 10, 1 );
        add_filter( 'wp_calculate_image_sizes', array( $this, 'calculate_content_image_sizes' ), 10, 2 );
    }
}