isotopsweden/wp-cargo

View on GitHub
src/class-cargo.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

namespace Isotop\Cargo;

use Frozzare\Tank\Container;
use Isotop\Cargo\Admin\Admin;
use Isotop\Cargo\Contracts\Driver_Interface;
use Closure;
use ReflectionClass;
use Exception;
use InvalidArgumentException;

class Cargo extends Container {

    /**
     * The Cargo configuration.
     *
     * @var array
     */
    protected $config = [];

    /**
     * The class instance.
     *
     * @var \Isotop\Cargo\Cargo
     */
    protected static $instance;

    /**
     * Get class instance.
     *
     * @return \Isotop\Cargo\Cargo
     */
    public static function instance() {
        if ( ! isset( static::$instance ) ) {
            static::$instance = new static;
        }

        return static::$instance;
    }

    /**
     * Add action hook that will work with Cargo.
     *
     * @param  string   $tag
     * @param  callable $fn
     * @param  int      $priority
     * @param  int      $accepted_args
     *
     * @throws Exception When WordPress is not loaded.
     *
     * @return bool
     */
    public function action( string $tag, callable $fn, int $priority = 10, int $accepted_args = 1 ) {
        $fn = Closure::bind( Closure::fromCallable( $fn ), $this );

        if ( ! function_exists( 'add_action' ) ) {
            throw new Exception( 'WordPress not loaded' );
        }

        return add_action( $tag, $fn, $priority, $accepted_args );
    }

    /**
     * Add filter hook that will work with Cargo.
     *
     * @param  string   $tag
     * @param  callable $fn
     * @param  int      $priority
     * @param  int      $accepted_args
     *
     * @throws Exception When WordPress is not loaded.
     *
     * @return bool
     */
    public function filter( string $tag, callable $fn, int $priority = 10, int $accepted_args = 1 ) {
        $fn = Closure::bind( Closure::fromCallable( $fn ), $this );

        if ( ! function_exists( 'add_filter' ) ) {
            throw new Exception( 'WordPress not loaded' );
        }

        return add_filter( $tag, $fn, $priority, $accepted_args );
    }

    /**
     * Get config value.
     *
     * @param  string     $key
     * @param  mixed|null $default
     * @param  array      $source
     *
     * @return mixed
     */
    public function config( string $key, $default = null, array $source = [] ) {
        if ( isset( $source[$key] ) ) {
            return $source[$key];
        }

        $keys = explode( '.', $key );

        if ( empty( $source ) ) {
            $source = $this->config;
        }

        foreach ( $keys as $i => $k ) {
            if ( ! isset( $source[$k] ) ) {
                continue;
            }

            unset( $keys[$i] );

            if ( is_array( $source[$k] ) && count( $keys ) > 0 ) {
                return $this->config( implode( '.', $keys ), $default, $source[$k] );
            }

            return $source[$k];
        }

        return $default;
    }

    /**
     * Set config by file or array.
     *
     * @param  string|mixed $config
     */
    public function set_config( $config ) {
        if ( is_string( $config ) && file_exists( $config ) ) {
            $config = require_once $config;
        }

        if ( is_array( $config ) ) {
            $this->config = array_replace_recursive( $this->config, $config );
        }
    }

    /**
     * Resolve the given type from the container.
     *
     * @param  string $id
     * @param  array  $parameters
     *
     * @throws InvalidArgumentException If identifier is not bound.
     *
     * @return mixed
     */
    public function make( $id, array $parameters = [] ) {
        try {
            return parent::make( $id, $parameters );
        }
        catch ( InvalidArgumentException $e ) {
            if ( $driver = $this->get_driver( $id ) ) {
                return $driver;
            }

            throw $e;
        }
    }

    /**
     * Get driver.
     *
     * @param  string $type
     *
     * @throws Exception If driver for given type can't be found.
     *
     * @return mixed
     */
    protected function get_driver( string $type ) {
        $driver = $this->config( sprintf( '%s.driver', $type ) );
        $key    = sprintf( 'driver.%s.%s', $type, $driver );

        if ( $this->bound( $key ) ) {
            return $this->make( $key );
        }

        throw new Exception( sprintf( '%s driver cannot be found', ucfirst( $type ) ) );
    }

    /**
     * Set driver.
     *
     * @param  string $key
     * @param  string $driver
     *
     * @throws Exception If the driver class cannot be found.
     * @throws Exception If the driver does not implement the driver interface.
     */
    public function set_driver( string $key, string $driver ) {
        if ( ! class_exists( $driver ) ) {
            throw new Exception( sprintf( 'Driver class for %s cannot be found', $key ) );
        }

        $key = sprintf( 'driver.%s', $key );
        $rc  = new ReflectionClass( $driver );
        $obj = $rc->newInstanceArgs( [$this] );

        if ( $obj instanceof Driver_Interface === false ) {
            throw new Exception( sprintf( '%s driver does not implement driver interface', $key ) );
        }

        $this->bind( $key, $obj );
    }
}