Pink-Crab/Perique-BladeOne-Provider

View on GitHub
src/BladeOne_Provider.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

declare( strict_types=1 );

/**
 * Implementation of BladeOne for the PinkCrab Perique frameworks Renderable interface
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author Glynn Quelch <glynn.quelch@gmail.com>
 * @license http://www.opensource.org/licenses/mit-license.html  MIT License
 * @package PinkCrab\BladeOne_Provider
 */

namespace PinkCrab\BladeOne;

use Exception;
use ReflectionClass;
use BadMethodCallException;
use eftec\bladeone\BladeOne;
use eftec\bladeonehtml\BladeOneHtml;
use PinkCrab\BladeOne\PinkCrab_BladeOne;
use PinkCrab\Perique\Interfaces\Renderable;
use PinkCrab\Perique\Services\View\View_Model;
use PinkCrab\Perique\Services\View\Component\Component;
use PinkCrab\Perique\Services\View\Component\Component_Compiler;

class BladeOne_Provider implements Renderable {

    /**
     * BladeOne Instance
     *
     * @var PinkCrab_BladeOne
     */
    protected static $blade;

    /**
     * Access to the component compiler.
     *
     * @var Component_Compiler
     */
    protected $component_compiler;

    /**
     * Creates an instance with blade one.
     *
     * @param PinkCrab_BladeOne $blade
     */
    final public function __construct( PinkCrab_BladeOne $blade ) {
        static::$blade = $blade;
    }

    /**
     * Static constructor with BladeOne initialisation details
     *
     * @param string|array<mixed> $template_path If null then it uses (caller_folder)/views
     * @param string $compiled_path If null then it uses (caller_folder)/compiles
     * @param int $mode =[BladeOne::MODE_AUTO,BladeOne::MODE_DEBUG,BladeOne::MODE_FAST,BladeOne::MODE_SLOW][$i]
     * @return self
     */
    public static function init(
        $template_path = null,
        ?string $compiled_path = null,
        int $mode = 0
    ): self {
        return new static( new PinkCrab_BladeOne( $template_path, $compiled_path, $mode ) );
    }

    /**
     * Returns the current BladeOne instance.
     *
     * @return BladeOne
     */
    public function get_blade(): BladeOne {
        return static::$blade;
    }

    /**
     * Returns the base view path.
     *
     * @return string
     * @since 1.4.0
     */
    public function base_view_path(): string {
        $paths = static::$blade->get_template_paths();
        return ! empty( $paths ) ? reset( $paths ) : '';
    }

    /**
     * Sets the esc function.
     *
     * @param string $esc
     * @return self
     */
    public function set_esc_function( string $esc ): self {
        static::$blade->set_esc_function( $esc );
        return $this;
    }

    /**
     * Sets the component compiler.
     *
     * @param Component_Compiler $compiler
     * @return void
     */
    public function set_component_compiler( Component_Compiler $compiler ): void {
        $this->component_compiler = $compiler;
    }

    /**
     * Display a view and its context.
     *
     * @param string $view
     * @param iterable<string, mixed> $data
     * @param bool $print
     * @return void|string
     */
    public function render( string $view, iterable $data, bool $print = true ) {
        if ( $print ) {
            print static::$blade->run( $view, (array) $data ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
        } else {
            return static::$blade->run( $view, (array) $data );
        }
    }

    /**
     * Renders a view Model
     *
     * @param View_Model $view_model
     * @return string|void
     */
    public function view_model( View_Model $view_model, bool $print = true ) {
        return $this->render( str_replace( array( '/', '\\' ), '.', $view_model->template() ), $view_model->data(), $print );
    }

        /**
     * Renders a component.
     *
     * @param Component $component
     * @return string|void
     */
    public function component( Component $component, bool $print = true ) {

        // Throw exception of no compiler passed.
        if ( ! is_a( $this->component_compiler, Component_Compiler::class ) ) {
            throw new Exception( 'No component compiler passed to BladeOne' );
        }

        // Compile the component.
        $compiled = $this->component_compiler->compile( $component );
        return $this->render( str_replace( array( '/', '\\' ), '.', $compiled->template() ), $compiled->data(), $print );
    }

    /**
     * magic instanced method caller.
     *
     * @param string $method
     * @param array<mixed> $args
     * @return mixed
     * @throws BadMethodCallException
     */
    public function __call( string $method, array $args = array() ) {
        if ( ! $this->is_method( $method ) ) {
            throw new BadMethodCallException( "{$method} is not a valid method on the BladeOne instance." );
        }

        return static::$blade->{$method}( ...$args );
    }

    /**
     * Magic static method caller.
     *
     * @param string $method
     * @param array<mixed> $args
     * @return mixed
     * @throws BadMethodCallException
     */
    public static function __callStatic( string $method, array $args = array() ) {
        if ( ! static::is_static_method( $method ) ) {
            throw new BadMethodCallException( "{$method} is not a valid method on the BladeOne instance." );
        }

        return static::$blade::{$method}( ...$args );
    }

    /**
     * Checks if the passed method exists, is public and isnt static.
     *
     * @param string $method
     * @return bool
     */
    protected function is_method( string $method ): bool {
        $class_reflection = new ReflectionClass( static::$blade );

        // Check method exists.
        if ( ! $class_reflection->hasMethod( $method ) ) {
            return false;
        }

        $method_reflection = $class_reflection->getMethod( $method );

        return $method_reflection->isPublic() && ! $method_reflection->isStatic();
    }

    /**
     * Checks if the passed method exists, is public and IS static.
     *
     * @param string $method
     * @return bool
     */
    protected static function is_static_method( string $method ): bool {
        $class_reflection = new ReflectionClass( static::$blade );

        // Check method exists.
        if ( ! $class_reflection->hasMethod( $method ) ) {
            return false;
        }

        $method_reflection = $class_reflection->getMethod( $method );
        return $method_reflection->isPublic() && $method_reflection->isStatic();
    }

    /**
     * Sets if piping is enabled in templates.
     *
     * @param bool $bool
     * @return self
     */
    public function allow_pipe( bool $bool = true ): self {
        static::$blade->pipeEnable = $bool; //phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
        return $this;
    }

    /**
     * Register a handler for custom directives.
     *
     * @param string   $name
     * @param callable $handler
     * @return self
     */
    public function directive( string $name, callable $handler ): self {
        static::$blade->directive( $name, $handler );
        return $this;
    }

    /**
     * Register a handler for custom directives for run at runtime
     *
     * @param string   $name
     * @param callable $handler
     * @return self
     */
    public function directive_rt( $name, callable $handler ): self {
        static::$blade->directiveRT( $name, $handler );
        return $this;
    }

    /**
     * Define a template alias
     *
     * @param string      $view  example "folder.template"
     * @param string|null $alias example "mynewop". If null then it uses the name of the template.
     * @return self
     */
    public function add_include( $view, $alias = null ): self {
        static::$blade->addInclude( $view, $alias );
        return $this;
    }

    /**
     * Define a class with a namespace
     *
     * @param string $alias_name
     * @param string $class_with_namespace
     * @return self
     */
    public function add_alias_classes( $alias_name, $class_with_namespace ): self {
        static::$blade->addAliasClasses( $alias_name, $class_with_namespace );
        return $this;
    }

    /**
     * Set the compile mode
     *
     * @param int $mode BladeOne::MODE_AUTO, BladeOne::MODE_DEBUG, BladeOne::MODE_FAST, BladeOne::MODE_SLOW
     * @return self
     */
    public function set_mode( int $mode ): self {
        static::$blade->setMode( $mode );
        return $this;
    }

    /**
     * Adds a global variable. If <b>$var_name</b> is an array then it merges all the values.
     * <b>Example:</b>
     * <pre>
     * $this->share('variable',10.5);
     * $this->share('variable2','hello');
     * // or we could add the two variables as:
     * $this->share(['variable'=>10.5,'variable2'=>'hello']);
     * </pre>
     *
     * @param string|array<string, mixed> $var_name It is the name of the variable or it is an associative array
     * @param mixed        $value
     * @return $this
     */
    public function share( $var_name, $value = null ): self {
        static::$blade->share( $var_name, $value );
        return $this;
    }

    /**
     * Sets the function used for resolving classes with inject.
     *
     * @param callable $function
     * @return $this
     */
    public function set_inject_resolver( callable $function ): self {
        static::$blade->setInjectResolver( $function );
        return $this;
    }

    /**
     * Set the file extension for the template files.
     * It must includes the leading dot e.g. .blade.php
     *
     * @param string $file_extension Example: .prefix.ext
     * @return $this
     */
    public function set_file_extension( string $file_extension ): self {
        static::$blade->setFileExtension( $file_extension );
        return $this;
    }

    /**
     * Set the file extension for the compiled files.
     * Including the leading dot for the extension is required, e.g. .bladec
     *
     * @param string $file_extension
     * @return $this
     */
    public function set_compiled_extension( string $file_extension ): self {
        static::$blade->setCompiledExtension( $file_extension );
        return $this;
    }

}