samsonos/php_core

View on GitHub
src/Core.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php declare(strict_types=1);
/*
 * This file is part of the SamsonPHP\Core package.
 * (c) 2013 Vitaly Iegorov <egorov@samsonos.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace samson\core;

use Doctrine\Common\Annotations\AnnotationReader;
use samsonframework\container\Builder;
use samsonframework\container\ContainerBuilderInterface;
use samsonframework\container\ContainerInterface;
use samsonframework\container\definition\analyzer\annotation\annotation\InjectService;
use samsonframework\container\metadata\ClassMetadata;
use samsonframework\container\metadata\MethodMetadata;
use samsonframework\container\metadata\PropertyMetadata;
use samsonframework\containerannotation\AnnotationClassResolver;
use samsonframework\containerannotation\AnnotationMetadataCollector;
use samsonframework\containerannotation\AnnotationMethodResolver;
use samsonframework\containerannotation\AnnotationPropertyResolver;
use samsonframework\containerannotation\AnnotationResolver;
use samsonframework\containerannotation\Injectable;
use samsonframework\containerannotation\InjectArgument;
use samsonframework\containercollection\attribute\ArrayValue;
use samsonframework\containercollection\attribute\ClassName;
use samsonframework\containercollection\attribute\Name;
use samsonframework\containercollection\attribute\Value;
use samsonframework\containercollection\CollectionClassResolver;
use samsonframework\containercollection\CollectionMethodResolver;
use samsonframework\containercollection\CollectionParameterResolver;
use samsonframework\containercollection\CollectionPropertyResolver;
use samsonframework\containerxml\XmlMetadataCollector;
use samsonframework\containerxml\XmlResolver;
use samsonframework\core\PreparableInterface;
use samsonframework\core\SystemInterface;
use samsonframework\resource\ResourceMap;
use samsonphp\config\Scheme;
use samsonphp\core\exception\CannotLoadModule;
use samsonphp\core\exception\ViewPathNotFound;
use samsonphp\core\Module;
use samsonphp\event\Event;
use samsonframework\container\definition\analyzer\annotation\annotation\Service as ServiceAnnotation;

/**
 * SamsonPHP Core.
 *
 * @author Vitaly Iegorov <egorov@samsonos.com>
 * @ServiceAnnotation("core")
 */
class Core implements SystemInterface
{
    /** @deprecated Standard algorithm for view rendering */
    const RENDER_STANDART = 1;
    /** @deprecated View rendering algorithm from array of view variables */
    const RENDER_VARIABLE = 3;
    /** @deprecated @var  ResourceMap Current web-application resource map */
    public $map;
    /** @deprecated @var string Path to current web-application */
    public $system_path = __SAMSON_CWD__;

    /* Rendering models */
    /** @deprecated @var string View path loading mode */
    public $render_mode = self::RENDER_STANDART;
    /** @var ContainerInterface */
    protected $container;
    /** @var ClassMetadata[] */
    protected $metadataCollection = [];
    /** @var ContainerBuilderInterface */
    protected $builder;
    /** @var string Current system environment */
    protected $environment;
    protected $classes = [];
    /** @var Module Pointer to current active module */
    protected $active = null;
    /** @var bool Flag for outputting layout template, used for asynchronous requests */
    protected $async = false;
    /** @var string Path to main system template */
    protected $template_path = __SAMSON_DEFAULT_TEMPLATE;

    /**
     * Core constructor.
     *
     * @param ContainerBuilderInterface $builder Container builder
     * @param ResourceMap|null $map system resources
     * @InjectService(container="container")
     * InjectArgument(builder="samsonframework\container\ContainerBuilderInterface")
     * InjectArgument(builder="samsonframework\core\ResourceInterface")
     */
    public function __construct(ContainerInterface $container, ContainerBuilderInterface $builder = null, ResourceMap $map = null)
    {
        $this->container = $container;
        $this->builder = $builder;
        // Get correct web-application path
        $this->system_path = __SAMSON_CWD__;

        // Get web-application resource map
        $this->map = ResourceMap::get($this->system_path, false, array('src/'));

        // Temporary add template worker
        $this->subscribe('core.rendered', array($this, 'generateTemplate'));

        // Fire core creation event
        Event::fire('core.created', array(&$this));

        // Signal core configure event
        Event::signal('core.configure', array($this->system_path . __SAMSON_CONFIG_PATH));
    }

    /**
     * Generic wrap for Event system subscription.
     * @see \samson\core\\samsonphp\event\Event::subscribe()
     *
     * @param string   $key     Event identifier
     * @param callable $handler Event handler
     * @param array    $params  Event parameters
     *
     * @return $this Chaining
     */
    public function subscribe($key, $handler, $params = array())
    {
        Event::subscribe($key, $handler, $params);

        return $this;
    }

    /**
     * Change current system working environment or receive
     * current system enviroment if no arguments are passed.
     *
     * @param string $environment Environment identifier
     *
     * TODO: Function has two different logics - needs to be changed!
     * @return $this|string Chaining or current system environment
     */
    public function environment($environment = Scheme::BASE)
    {
        if (func_num_args() !== 0) {
            $this->environment = $environment;

            // Signal core environment change
            Event::signal('core.environment.change', array($environment, &$this));
            return $this;
        }

        return $this->environment;
    }

    /**
     * Generate special response header triggering caching mechanisms
     * @param int $cacheLife Amount of seconds for cache(default 3600 - 1 hour)
     * @param string $accessibility Cache-control accessibility value(default public)
     */
    public function cached($cacheLife = 3600, $accessibility = 'public')
    {
        static $cached;
        // Protect sending cached headers once
        if (!isset($cached) or $cached !== true) {
            header('Expires: ' . gmdate('D, d M Y H:i:s T', time() + $cacheLife));
            header('Cache-Control: ' . $accessibility . ', max-age=' . $cacheLife);
            header('Pragma: cache');

            $cached = true;
        }
    }

    /**
     * Set asynchronous mode.
     * This mode will not output template and will just path everything that
     * was outputted to client.
     *
     * @param bool $async True to switch to asynchronous output mode
     *
     * @return $this Chaining
     */
    public function async($async)
    {
        $this->async = $async;

        return $this;
    }

    /**    @see iModule::active() */
    public function active($module = null)
    {
        // Сохраним старый текущий модуль
        $old = $this->active;

        // Если передано значение модуля для установки как текущий - проверим и установим его
        if (isset($module)) {
            $this->active = $module;
        }

        // Вернем значение текущего модуля
        return $old;
    }

    /**
     * Retrieve module instance by identifier.
     *
     * @param string|null $module Module identifier
     *
     * @return null|Module Found or active module
     */
    public function module($module = null)
    {
        $return = null;

        // Ничего не передано - вернем текущуй модуль системы
        if (!isset($module) && isset($this->active)) {
            $return = &$this->active;
        } elseif (is_object($module)) {
            $return = &$module;
        } elseif (is_string($module)) {
            $return = $this->container->get($module);
        }

//        // Ничего не получилось вернем ошибку
        if ($return === null) {
            e('Не возможно получить модуль(##) системы', E_SAMSON_CORE_ERROR, array($module));
        }

        return $return;
    }

    /**
     * Unload module from core.
     *
     * @param string $moduleID Module identifier
     */
    public function unload($moduleID)
    {
        if (isset($this->module_stack[$moduleID])) {
            unset($this->module_stack[$moduleID]);
        }
    }

    /**
     * Insert generic html template tags and data
     *
     * @param string $templateHtml Generated HTML
     *
     * @deprecated Must be moved to a new HTML output object
     * @return mixed Changed HTML template
     */
    public function generateTemplate(&$templateHtml)
    {
        // Добавим путь к ресурсам для браузера
        $headHtml = "\n" . '<base href="' . url()->base() . '">';
        // Добавим отметку времени для JavaScript
        $headHtml .= "\n" . '<script type="text/javascript">var __SAMSONPHP_STARTED = new Date().getTime();</script>';

        // Добавим поддержку HTML для старых IE
        $headHtml .= "\n" . '<!--[if lt IE 9]>';
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>';
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>';
        $headHtml .= "\n" . '<![endif]-->';

        // Выполним вставку главного тега <base> от которого зависят все ссылки документа
        // также подставим МЕТА-теги для текущего модуля и сгенерированный минифицированный CSS
        $templateHtml = str_ireplace('<head>', '<head>' . $headHtml, $templateHtml);

        // Вставим указатель JavaScript ресурсы в конец HTML документа
        $templateHtml = str_ireplace('</html>', '</html>' . __SAMSON_COPYRIGHT, $templateHtml);

        return $templateHtml;
    }

    /**
     * Start SamsonPHP framework.
     *
     * @param string $default Default module identifier
     *
     * @throws ViewPathNotFound
     */
    public function start($default)
    {
        // TODO: Change ExternalModule::init() signature
        // Fire core started event
        Event::fire('core.started');

        // TODO: Does not see why it should be here
        // Set main template path
        $this->template($this->template_path);

        // Security layer
        $securityResult = true;
        // Fire core security event
//        Event::fire('core.security', array(&$this, &$securityResult));

        /** @var mixed $result External route controller action result */
        $result = false;

        // If we have passed security application layer
        if ($securityResult) {
            // Fire core routing event - go to routing application layer
            Event::signal('core.routing', array(&$this, &$result, $default));
        }

        // If no one has passed back routing callback
        if (!isset($result) || $result === false) {
            // Fire core e404 - routing failed event
            $result = Event::signal('core.e404', array(url()->module, url()->method));
        }

        // Response
        $output = '';

        // If this is not asynchronous response and controller has been executed
        if (!$this->async && ($result !== false)) {
            // Store module data
            $data = $this->active->toView();

            // Render main template
            $output = $this->render($this->template_path, $data);

            // Fire after render event
            Event::fire('core.rendered', array(&$output));
        }

        // Output results to client
        echo $output;

        // Fire ended event
        Event::fire('core.ended', array(&$output));
    }

    /**    @see iCore::template() */
    public function template($template = NULL, $absolutePath = false)
    {
        // Если передан аргумент
        if( func_num_args() ){
            $this->template_path = ($absolutePath)?$template: $this->active->path().$template;
        }

        // Аргументы не переданы - вернем текущий путь к шаблону системы
        return $this->template_path;
    }

    /**
     * Render file to a buffer.
     *
     * @param string $view Path to file
     * @param array  $data Collection of variables to path to file
     *
     * @return string Rendered file contents
     * @throws ViewPathNotFound
     */
    public function render($view, $data = array())
    {
        // TODO: Make rendering as external system, to split up these 3 rendering options

        // Объявить ассоциативный массив переменных в данном контексте
        if (is_array($data)) {
            extract($data);
        }

        // Начать вывод в буффер
        ob_start();

        // Path to another template view, by default we are using default template folder path,
        // for meeting first condition
        $templateView = $view;

        if (locale() != SamsonLocale::DEF) {
            // Modify standard view path with another template
            $templateView = str_replace(__SAMSON_VIEW_PATH, __SAMSON_VIEW_PATH . locale() . '/', $templateView);
        }

        // Depending on core view rendering model
        switch ($this->render_mode) {
            // Standard algorithm for view rendering
            case self::RENDER_STANDART:
                // Trying to find another template path, by default it's an default template path
                if (file_exists($templateView)) {
                    include($templateView);
                } elseif (file_exists($view)) {
                    // If another template wasn't found - we will use default template path
                    include($view);
                } else { // Error no template view was found
                    throw(new ViewPathNotFound($view));
                }
                break;

            // View rendering algorithm from array of view variables
            case self::RENDER_VARIABLE:
                // Collection of views
                $views = &$GLOBALS['__compressor_files'];
                // Trying to find another template path, by default it's an default template path
                if (isset($views[$templateView])) {
                    eval(' ?>' . $views[$templateView] . '<?php ');
                } elseif (isset($views[$view])) {
                    // If another template wasn't found - we will use default template path
                    eval(' ?>' . $views[$view] . '<?php ');
                } else { // Error no template view was found
                    throw(new ViewPathNotFound($view));
                }
                break;
        }

        // Получим данные из буффера вывода
        $html = ob_get_contents();

        // Очистим буффер
        ob_end_clean();

        // Fire core render event
        Event::fire('core.render', array(&$html, &$data, &$this->active));

        ////elapsed('End rendering '.$__view);
        return $html;
    }

    /**
     * Load system from composer.json
     * @param string $dependencyFilePath Path to dependencies file
     * @return $this Chaining
     * @deprecated
     */
    public function composer($dependencyFilePath = null)
    {

        $this->active = new VirtualModule($this->system_path, $this->map, $this, 'local');

        return $this;
    }


    //[PHPCOMPRESSOR(remove,start)]

    /** @see iCore::path() */
    public function path($path = null)
    {
        // Если передан аргумент
        if (func_num_args()) {
            // Сформируем новый относительный путь к главному шаблону системы
            $this->template_path = $path . $this->template_path;

            // Сохраним относительный путь к Веб-приложению
            $this->system_path = $path;

            // Продолжил цепирование
            return $this;
        }

        // Вернем текущее значение
        return $this->system_path;
    }

    //[PHPCOMPRESSOR(remove,end)]

    /** @return \Container Get system container */
    public function getContainer()
    {
        return $this->container;
    }

    /** Магический метод для десериализации объекта */
    public function __wakeup()
    {
        $this->active = &$this->module_stack['local'];
    }

    /** Магический метод для сериализации объекта */
    public function __sleep()
    {
        return array('module_stack', 'render_mode');
    }
}