atelierspierrot/library

View on GitHub
src/Library/Reporter/Reporter.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php
/**
 * This file is part of the Library package.
 *
 * Copyleft (ↄ) 2013-2016 Pierre Cassat <me@e-piwi.fr> and contributors
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * The source code of this package is available online at 
 * <http://github.com/atelierspierrot/library>.
 */

namespace Library\Reporter;

/**
 * @author  piwi <me@e-piwi.fr>
 */
class Reporter
{

    /**
     * Set the reporter to return each output rendering
     */
    const OUTPUT_BY_LINE = 1;

    /**
     * Set the reporter to append each output rendering to a global output
     */
    const OUTPUT_APPEND = 2;

    /**
     * @var string The reporter global output
     */
    protected $output;

    /**
     * @var int The reporter flag (must be one of the class OUTPUT constants)
     */
    protected $flag;

    /**
     * @var \Library\Reporter\Adapter\... The reporter adapter
     */
    protected $__adapter;

    /**
     * @var array Table of the default tag types the adapters must implement
     */
     public static $default_tag_types = array(
        'default',
        'unordered_list',
        'ordered_list',
        'table',
        'definition',
        'code',
        'pre_formated',
        'title',
        'paragraph',
        'citation',
        'bold',
        'italic',
        'link',
     );

    /**
     * @var array Table of the default masks the adapters must implement as constants
     */
      public static $default_masks = array(
        'default',
        'new_line',
        'tab',
        'key_value',
        'unordered_list',
        'unordered_list_item',
        'ordered_list',
        'ordered_list_item',
        'table',
        'table_title',
        'table_head',
        'table_head_line',
        'table_head_cell',
        'table_body',
        'table_body_line',
        'table_body_cell',
        'table_foot',
        'table_foot_line',
        'table_foot_cell',
        'definition',
        'definition_term',
        'definition_description',
        'code',
        'pre_formated',
        'title',
        'paragraph',
        'citation',
        'bold',
        'italic',
        'link',
     );

    /**
     * Construction of a new Reporter object
     *
     * @param   null|string     $adapter_type The adapter type name
     * @param   int             $flag
     */
    public function __construct($adapter_type = 'html', $flag = self::OUTPUT_BY_LINE)
    {
        $this->setAdapterType($adapter_type);
        $this->setFlag($flag);
    }

    /**
     * Reset all object properties to default or empty values
     *
     * @param bool $hard Reset all object properties (adapter included)
     * @return self $this for method chaining
     */
    public function reset($hard = false)
    {
        $this->output = '';
        if (true===$hard) {
            $this->__adapter_type = null;
            $this->__adapter = null;
        }
        return $this;
    }

    /**
     * Returns the object global output
     *
     * @return string
     */
    public function __toString()
    {
        return $this->getOutput();
    }

// -------------------------
// Getters / Setters
// -------------------------

    /**
     * Set the reporter flag
     *
     * @param int $flag The flag to set
     * @return self Returns `$this` for method chaining
     */
    public function setFlag($flag)
    {
        $this->flag = $flag;
        return $this;
    }

    /**
     * Get the reporter flag
     *
     * @return int
     */
    public function getFlag()
    {
        return $this->flag;
    }

    /**
     * Set the adapter type to use
     *
     * @param string $type The type name
     * @return self $this for method chaining
     * @throws Throws a RuntimeException if the adapter doesn't exist
     */
    public function setAdapterType($type)
    {
        $adapter_type = '\Library\Reporter\Adapter\\'.ucfirst($type);
        if (class_exists($adapter_type)) {
            $this->setAdapter(new $adapter_type);
        } else {
            throw new \RuntimeException(
                sprintf('Reporter adapter for type "%s" doesn\'t exist!', $adapter_type)
            );
        }
        return $this;
    }

    /**
     * Get the current adapter name
     *
     * @return object
     */
    public function getAdapterType()
    {
        return !empty($this->__adapter) ? get_class($this->__adapter) : null;
    }

    /**
     * Set the adapter
     *
     * @param   \Library\Reporter\AbstractAdapter $adapter The instance of a ReporterAdapter
     * @return  self
     * @throws  \LogicException
     */
    public function setAdapter(AbstractAdapter $adapter)
    {
        $cls = get_class($adapter);
        foreach(self::$default_masks as $_mask) {
            if (null===@constant($cls.'::mask_'.$_mask)) {
                throw new \LogicException(
                    sprintf('Reporter adapter "%s" must define a mask named "%s"!', $cls, $_mask)
                );
            }
        }
        $this->__adapter = $adapter;
        return $this;
    }

    /**
     * Get the current adapter
     *
     * @return object
     */
    public function getAdapter()
    {
        return $this->__adapter;
    }

    /**
     * Set some content
     *
     * @param string $output The content string
     * @return self Returns `$this` for method chaining
     */
    public function setOutput($output)
    {
        if ($this->getFlag() & self::OUTPUT_APPEND) {
            $this->output .= $output;
        } elseif ($this->getFlag() & self::OUTPUT_BY_LINE) {
            $this->output = $output;
        }
        return $this;
    }

    /**
     * Get the processed content
     *
     * @return string The content string
     */
    public function getOutput()
    {
        return $this->output;
    }

// -------------------------
// Rendering
// -------------------------

    /**
     * Render a content with a specific tag mask
     *
     * @param string $content The content string to use
     * @param string $tag_type The type of tag mask to use
     * @param string|array $args An array of arguments to pass to the mask (or a single string that
     *                          will be taken as the first array item)
     * @return string|self Returns the line of output if the object flag is set on `OUTPUT_BY_LINE`,
     *                      or `$this` if the flag is set on `OUTPUT_APPEND`
     */
    public function render($content, $tag_type = 'default', $args = null)
    {
        if (is_null($args)) $args = array();
        if (!is_array($args)) $args = array( $args );
        $output = $this->getAdapter()->renderTag($content, $tag_type, $args);
        $this->setOutput($output);
        if ($this->getFlag() & self::OUTPUT_APPEND) {
            return $this;
        } elseif ($this->getFlag() & self::OUTPUT_BY_LINE) {
            return $output;
        }
    }

    /**
     * Display on screen a content with a specific tag mask
     *
     * @param string $content The content string to use
     * @param string $tag_type The type of tag mask to use
     * @param string|array $args An array of arguments to pass to the mask (or a single string that
     *                          will be taken as the first array item)
     * @return void
     */
    public function write($content, $tag_type = 'default', $args = null)
    {
        if (is_null($args)) $args = array();
        if (!is_array($args)) $args = array( $args );
        $output = $this->getAdapter()->renderTag($content, $tag_type, $args);
        echo PHP_EOL.$output.PHP_EOL;
    }

    /**
     * Render a content with a specific tag mask and some placeholders
     *
     * This is quite the same as the `render()` method but in this case, the `$content` string
     * may contains some placeholders like `@name@` that will be replaced in the result by
     * the `name` item of the `$multi` array argument after rendering it by the `render()` method.
     *
     * For instance:
     *
     *     $str = $obj->renderMulti( 'my string with @name@ placeholder', 'default', array(
     *         'name' => array( 'a specific string as' , 'strong' )
     *     ));
     *
     * will return:
     *
     *     "<p>my string with <strong>a specific string as</strong> placeholder</p>"
     *
     * @param string $content The content string to use
     * @param string $tag_type The type of tag mask to use
     * @param array $multi The array of imbricated elements for content replacements
     * @param string|array $args An array of arguments to pass to the mask (or a single string that
     *                          will be taken as the first array item)
     * @param string $placeholder_mask The mask used to build placeholders names in `$content`
     * @return string|self Returns the line of output if the object flag is set on `OUTPUT_BY_LINE`,
     *                      or `$this` if the flag is set on `OUTPUT_APPEND`
     */
    public function renderMulti($content, $tag_type = 'default', array $multi = array(), $args = null, $placeholder_mask = '@%s@')
    {
        if (is_null($args)) $args = array();
        if (!is_array($args)) $args = array( $args );

        // rendering all placeholders
        $placeholders_table = array();
        foreach($multi as $item=>$item_args) {
            $placeholders_table[$item] = call_user_func_array(
                array($this->getAdapter(), 'renderTag'),
                $item_args
            );
        }

        // replacing placeholders
        $full_content = $content;
        foreach ($placeholders_table as $name => $value) {
            $full_content = strtr($full_content, array(sprintf($placeholder_mask, $name) => $value));
        }

        // rendering final output
        $output = $this->getAdapter()->renderTag($full_content, $tag_type, $args);

        $this->setOutput($output);
        if ($this->getFlag() & self::OUTPUT_APPEND) {
            return $this;
        } elseif ($this->getFlag() & self::OUTPUT_BY_LINE) {
            return $output;
        }
    }

}