<?php

/**
 * DframeFramework
 * Copyright (c) Sławomir Kaleta.
 *
 * @license https://github.com/dframe/dframe/blob/master/LICENCE (MIT)
 */

namespace Dframe;

/**
 * Config Class.
 *
 * @author Sławomir Kaleta <slaszka@gmail.com>
 */
class Config
{
    /**
     * @var array
     */
    protected static $cfg = [];

    /**
     * @var string
     */
    public $path;

    /**
     * @var string
     */
    protected $file;

    /**
     * Config constructor.
     *
     * @param        $file
     * @param string $path
     */
    public function __construct($file, $path = '')
    {
        $this->path = (isset($path) and !empty($path)) ? $path : APP_DIR . $path . 'Config' . DIRECTORY_SEPARATOR;

        $this->file = $file;
        if (file_exists($this->path . $this->file . '.php') != true) {
            self::$cfg[$file] = [];
        } else {
            self::$cfg[$file] = isset(self::$cfg[$file]) ? self::$cfg[$file] : include $this->path . $this->file . '.php';
        }
    }

    /**
     * @param      $file
     * @param null $path
     *
     * @return Config
     */
    public static function load($file, $path = null)
    {
        return new self($file, $path);
    }

    /**
     * @param null $param
     * @param null $or
     *
     * @return mixed|null
     */
    public function get($param = null, $or = null)
    {
        if ($param === null) {
            return (isset(self::$cfg[$this->file])) ? self::$cfg[$this->file] : null;
        }

        return (isset(self::$cfg[$this->file][$param]) and !empty(self::$cfg[$this->file][$param])) ? self::$cfg[$this->file][$param] : $or;
    }
}


<?php

/**
 * DframeFramework
 * Copyright (c) Sławomir Kaleta.
 *
 * @license https://github.com/dframe/dframe/blob/master/LICENCE (MIT)
 */

namespace Dframe\View;

use Dframe\Config;
use Dframe\View\Exceptions\ViewException;

/**
 * Default View.
 *
 * @author Sławomir Kaleta <slaszka@gmail.com>
 * @author Amadeusz Dzięcioł <amadeusz.xd@gmail.com>
 */
class DefaultView implements ViewInterface
{
    /**
     * @var Config
     */
    private $templateConfig;

    /**
     * DefaultView constructor.
     */
    public function __construct()
    {
        $this->templateConfig = Config::load('view/default');
    }

    /**
     * Set the var to the template.
     *
     * @param string $name
     * @param string $value
     *
     * @return void
     */
    public function assign($name, $value)
    {
        $this->$name = $value;
    }

    /**
     * Return code.
     *
     * @param string $name Filename
     * @param string $path Alternative Path
     *
     * @return string
     */
    public function fetch($name, $path = null)
    {
        $pathFile = pathFile($name);
        $folder = $pathFile[0];
        $name = $pathFile[1];

        if ($path === null) {
            $path = $this->templateConfig->get('setTemplateDir') . DIRECTORY_SEPARATOR . $folder . $name . $this->templateConfig->get('fileExtension', '.html.php');
        }

        try {
            if (!is_file($path)) {
                throw new ViewException('Can not open template ' . $name . ' in: ' . $path);
            }
            ob_start();
            include $path;
        } catch (ViewException $e) {
            echo $e->getMessage() . '

                File: ' . $e->getFile() . '

                Code line: ' . $e->getLine() . '

                Trace: ' . $e->getTraceAsString();
            exit();
        }

        return ob_get_clean();
    }

    /**
     * Return code to the Smarty template.
     *
     * @param string $name
     * @param string $path
     *
     * @return mixed
     */
    public function renderInclude($name, $path = null)
    {
        $pathFile = pathFile($name);
        $folder = $pathFile[0];
        $name = $pathFile[1];

        if ($path === null) {
            $path = $this->templateConfig->get('setTemplateDir') . DIRECTORY_SEPARATOR . $folder . $name . $this->templateConfig->get('fileExtension', '.html.php');
        }

        try {
            if (!is_file($path)) {
                throw new ViewException('Can not open template ' . $name . ' in: ' . $path);
            }

            $renderInclude = include $path;
        } catch (ViewException $e) {
            echo $e->getMessage() . '

                File: ' . $e->getFile() . '

                Code line: ' . $e->getLine() . '

                Trace: ' . $e->getTraceAsString();
            exit();
        }

        return $renderInclude;
    }
}


<?php

/**
 * DframeFramework
 * Copyright (c) Sławomir Kaleta.
 *
 * @license https://github.com/dframe/dframe/blob/master/LICENCE (MIT)
 */

namespace Dframe;

use Dframe\Router\Response;
use Dframe\View\Exceptions\ViewException;
use Dframe\View\ViewInterface;

/**
 * View Class.
 *
 * @author Sławomir Kaleta <slaszka@gmail.com>
 */
abstract class View extends Loader implements ViewInterface
{
    /**
     * Path Templates
     *
     * @var string
     */
    public $dir;

    /**
     * Defines template variables.
     *
     * @param string $name
     * @param mixed  $value
     *
     * @return mixed
     */
    public function assign($name, $value)
    {
        if (!isset($this->view)) {
            throw new ViewException('Please Define view engine in app/View.php', 500);
        }

        return $this->view->assign($name, $value);
    }

    /**
     * Generates the output of the templates with parsing all the template variables.
     *
     * @param string|array $data
     * @param string       $type
     *
     * @return mixed
     */
    public function render($data, $type = null)
    {
        if (empty($type) or $type === 'html') {
            return Response::Create($this->renderInclude($data));
        } elseif ($type === 'jsonp') {
            return $this->renderJSONP($data);
        } else {
            return $this->renderJSON($data);
        }
    }

    /**
     * File including
     *
     * @param string $name
     * @param null   $path
     *
     * @return mixed
     */
    public function renderInclude($name, $path = null)
    {
        if (!isset($this->view)) {
            throw new ViewException('Please Define view engine in app/View.php', 500);
        }

        if (!is_null($this->dir)) {
            $this->view->setTemplateDir($this->dir);
        }

        return $this->view->renderInclude($name, $path);
    }

    /**
     * Display JSONP.
     *
     * @param array $data
     */
    public function renderJSONP($data)
    {
        $callback = null;
        if (isset($_GET['callback'])) {
            $callback = $_GET['callback'];
        }

        exit(Response::Create($callback . '(' . json_encode($data) . ')')->headers(['Content-Type' => 'application/jsonp'])->display());
    }

    /**
     * Display JSON.
     *
     * @param array $data
     * @param int   $status
     */
    public function renderJSON($data, $status = 200)
    {
        exit(Response::Create(json_encode($data))->status($status)->headers(['Content-Type' => 'application/json'])->display());
    }

    /**
     * Fetch the output of the templates with parsing all the template variables.
     *
     * @param string $name
     * @param string $path
     *
     * @return mixed
     */
    public function fetch($name, $path = null)
    {
        if (!isset($this->view)) {
            throw new ViewException('Please Define view engine in app/View.php', 500);
        }

        if (!is_null($this->dir)) {
            $this->view->setTemplateDir($this->dir);
        }

        return $this->view->fetch($name, $path);
    }
}


<?php

/**
 * DframeFramework
 * Copyright (c) Sławomir Kaleta
 *
 * @license https://github.com/dframe/dframe/blob/master/LICENCE (MIT)
 */

namespace Dframe\Cron;

/**
 * Config Class
 *
 * @author Sławomir Kaleta <slaszka@gmail.com>
 */
abstract class Task extends \Dframe\Controller
{
    /**
     * @param $path
     *
     * @throws \Exception
     */
    private function checkDir($path)
    {
        if (!is_dir($path)) {
            if (!mkdir($path, 0777, true)) {
                throw new \Exception('Unable to create' . $path, '', 403);
            }
        }
    }

    /**
     * @param     $key
     * @param int $ttl
     *
     * @return bool
     * @throws \Exception
     */
    protected function lockTime($key, $ttl = 59)
    {
        $dir = $dirLog = APP_DIR . 'View/cache/logs/';
        $file = $key . '.txt';
        $this->checkDir($dir);
        $dirLog = $dir . $file;

        if (file_exists($dirLog) and filemtime($dirLog) + $ttl > time()) {
            return false;
        }

        $fp = fopen($dirLog, "w");
        fwrite($fp, date("d-m-Y H:i:s"));
        fclose($fp);

        return true;
    }

    /**
     * @param       $key
     * @param       $callback
     * @param array $bind
     * @param int   $ttl
     *
     * @return array
     * @throws \Exception
     */
    protected function inLock($key, $callback, array $bind = [], $ttl = 3600)
    {
        $dir = APP_DIR . 'View/cache/logs/';
        $file = $key . '.txt';
        $this->checkDir($dir);
        $dirLog = $dir . $file;

        if (!is_callable($callback)) {
            throw new \InvalidArgumentException;
        }

        $fp = fopen($dirLog, "w");
        if (flock($fp, LOCK_EX | LOCK_NB)) { // do an exclusive lock
            $data = call_user_func_array($callback, $bind);

            flock($fp, LOCK_UN); // release the lock
            $this->lockTime($key, $ttl);
        } else {
            return ['return' => false];
        }
        fwrite($fp, date("d-m-Y H:i:s"));
        fclose($fp);
        return ['return' => true, 'response' => $data];
    }
}


<?php

/**
 * DframeFramework
 * Copyright (c) Sławomir Kaleta.
 *
 * @license https://github.com/dframe/dframe/blob/master/LICENCE (MIT)
 */

namespace Dframe;

use Dframe\Router\Response;

/**
 * Core Class.
 *
 * @author Sławomir Kaleta <slaszka@gmail.com>
 */
class Core extends Loader
{

    /**
     * Display Controller result
     *
     * @param null  $controller
     * @param null  $action
     * @param array $args
     *
     * @return mixed
     */
    public function run($controller = null, $action = null, $args = [])
    {
        $this->router = $this->router->boot($this);

        if (is_null($controller ?? null) and is_null($action ?? null)) {
            $this->router->parseGets();
            $controller = $this->router->controller;
            $action = $this->router->action;
            $namespace = $this->router->namespace;
        }

        $loader = new Loader($this->baseClass);

        $Controller = $loader->loadController($controller, $namespace ?? '\\');
        $response = [];

        if (method_exists($Controller, 'start')) {
            $response[] = ['start', []];
        }

        if (method_exists($Controller, 'init')) {
            $response[] = ['init', []];
        }

        if (method_exists($Controller, $action) or is_callable([$Controller, $action])) {
            $response[] = [$action, $args];
        }

        if (method_exists($Controller, 'end')) {
            $response[] = ['end', []];
        }

        foreach ($response as $key => $data) {
            $run = call_user_func_array([$Controller, $data[0]], $data[1]);
            if ($run instanceof Response) {
                if (isset($this->debug)) {
                    $this->debug->addHeader(['X-DF-Debug-Controller' => $controller]);
                    $this->debug->addHeader(['X-DF-Debug-Method' => $action]);

                    $run->headers($this->debug->getHeader());
                }

                return $run->display();
            }
        }

        return true;
    }
}


<?php

/**
 * DframeFramework
 * Copyright (c) Sławomir Kaleta.
 *
 * @license https://github.com/dframe/dframe/blob/master/LICENCE (MIT)
 */

namespace Dframe\View;

use Dframe\Config;
use Dframe\View\Exceptions\ViewException;

/**
 * Smarty View.
 *
 * @author Sławomir Kaleta <slaszka@gmail.com>
 */
class SmartyView implements ViewInterface
{
    /**
     * @var \Smarty
     */
    public $smarty;

    /**
     * @var Config
     */
    protected $smartyConfig;

    /**
     * SmartyView constructor.
     */
    public function __construct()
    {
        $this->smartyConfig = Config::load('view/smarty');

        $smarty = new \Smarty;
        $smarty->debugging = $this->smartyConfig->get('debugging', false);
        $smarty->setTemplateDir($this->smartyConfig->get('setTemplateDir'))
            ->setCompileDir($this->smartyConfig->get('setCompileDir'))
            ->addPluginsDir($this->smartyConfig->get('addPluginsDir'));

        $this->smarty = $smarty;
    }

    /**
     * @param $dir
     */
    public function setTemplateDir($dir)
    {
        $this->smarty->setTemplateDir($dir);
    }

    /**
     * Set the var to the template.
     *
     * @param string $name
     * @param string $value
     *
     * @return mixed
     */
    public function assign($name, $value)
    {
        try {
            if ($this->smarty->getTemplateVars($name) !== null) {
                throw new ViewException('You can\'t assign "' . $name . '" in Smarty');
            }

            $assign = $this->smarty->assign($name, $value);
        } catch (ViewException $e) {
            echo $e->getMessage() . '

                File: ' . $e->getFile() . '

                Code line: ' . $e->getLine() . '

                Trace: ' . $e->getTraceAsString();
            exit();
        }

        return $assign;
    }

    /**
     * Return code.
     *
     * @param string $name Filename
     * @param string $path Alternative Path
     *
     * @return mixed
     */
    public function fetch($name, $path = null)
    {
        $pathFile = pathFile($name);
        $folder = $pathFile[0];
        $name = $pathFile[1];

        if ($path === null) {
            $path = $this->smarty->getTemplateDir(0) . DIRECTORY_SEPARATOR . $folder . $name . $this->smartyConfig->get('fileExtension', '.html.php');
        }

        try {
            if (!is_file($path)) {
                throw new ViewException('Can not open template ' . $name . ' in: ' . $path);
            }

            // Loading view
            $fetch = $this->smarty->fetch($path);
        } catch (\Exception $e) {
            echo $e->getMessage() . '

                File: ' . $e->getFile() . '

                Code line: ' . $e->getLine() . '

                Trace: ' . $e->getTraceAsString();
            exit();
        }

        return $fetch;
    }

    /**
     * Transfers the code to the Smarty template.
     *
     * @param string $name
     * @param string $path
     *
     * @return mixed
     */
    public function renderInclude($name, $path = null)
    {
        $pathFile = pathFile($name);
        $folder = $pathFile[0];
        $name = $pathFile[1];

        if ($path === null) {
            $path = $this->smarty->getTemplateDir(0) . DIRECTORY_SEPARATOR . $folder . $name . $this->smartyConfig->get('fileExtension', '.html.php');
        }

        try {
            if (!is_file($path)) {
                throw new ViewException('Can not open template ' . $name . ' in: ' . $path);
            }

            return $this->smarty->fetch($path); // Loading view
        } catch (ViewException $e) {
            echo $e->getMessage() . '

                File: ' . $e->getFile() . '

                Code line: ' . $e->getLine() . '

                Trace: ' . $e->getTraceAsString();
            exit();
        }
    }
}


<?php

/**
 * DframeFramework
 * Copyright (c) Sławomir Kaleta
 *
 * @license https://github.com/dframe/dframe/blob/master/LICENCE (MIT)
 */

namespace Dframe\Modules;

/**
 * Manager Module
 *
 * @author Sławomir Kaleta <slaszka@gmail.com>
 */

class ManagerModule
{
    public $app;

    /**
     * ManagerModule constructor.
     *
     * @param $app
     */
    public function __construct($app)
    {
        $this->app = $app;
    }

    /**
     * Load router.
     *
     * @param  string $path
     *
     * @return void
     */
    protected function loadRoutes($path)
    {
        $this->app->config['router']['routes'] = array_merge($this->app->config['router']['routes'] ?? [], (require $path)['routes']);
    }

    /**
     * Load model.
     *
     * @param  array $path
     *
     * @return void
     */
    protected function loadModels($path)
    {
        $this->app->config['model'] = array_unique(array_merge($this->app->config['model'] ?? [], $path));
    }

    /**
     * Load controller.
     *
     * @param  array $path
     *
     * @return void
     */
    protected function loadControllers($path)
    {
        $this->app->config['controller'] = array_unique(array_merge($this->app->config['controller'] ?? [], $path));
    }
}


<?php

/**
 * DframeFramework
 * Copyright (c) Sławomir Kaleta.
 *
 * @license https://github.com/dframe/dframe/blob/master/LICENCE (MIT)
 */

namespace Dframe;

use Dframe\Router\Exceptions\InvalidArgumentException;
use Dframe\Router\Exceptions\RuntimeException;
use Dframe\Router\Response;

/**
 * Router class.
 *
 * @author Sławomir Kaleta <slaszka@gmail.com>
 */
class Router
{
    /**
     * @var array
     */
    public $routeMap = [];

    /**
     * Delay Redirect.
     *
     * @var mixed
     */
    public $delay = null;

    /**
     * @var string[]
     */
    public $parseArgs = [];

    /**
     * @var Config
     */
    public $routerConfig;

    /**
     * @var bool
     */
    public $https;

    /**
     * @var string
     */
    public $requestPrefix;

    /**
     * @var object
     */

    public $app;

    /**
     * @var string
     */
    public $namespace;

    /**
     * @var string
     */
    public $controller;

    /**
     * @var string
     */
    public $action;

    /**
     * @var array
     */
    protected $routeMapParse = [];

    /**
     * @var string
     */
    protected $uri;

    /**
     * @var string|null
     */
    protected $subdomain = null;

    /**
     * @var string
     */
    protected $routesFile = 'routes.php';

    /**
     * @var string
     */
    protected $controllersFile = 'controllers.php';

    /**
     * @var string[]
     */
    protected $usedControllers = [];

    /**
     * @var array
     */
    protected $controllerDirs;

    /**
     * @var string
     */
    protected $cacheDir;

    /**
     * @var string
     */
    protected $domain;

    /**
     * Router constructor.
     */
    public function __construct()
    {
        if (!defined('HTTP_HOST') and isset($_SERVER['HTTP_HOST'])) {
            define('HTTP_HOST', $_SERVER['HTTP_HOST']);
        } elseif (!defined('HTTP_HOST')) {
            define('HTTP_HOST', '');
        }

        $this->domain = HTTP_HOST;
        $uri = explode('/', $_SERVER['SCRIPT_NAME']);
        array_pop($uri);
        $this->uri = implode('/', $uri) . '/';
        $this->uri = str_replace('/web/', '/', $this->uri);

        $this->routerConfig = Config::load('router');

        $this->setHttps($this->routerConfig->get('https', false));
        $this->routeMap = $this->routerConfig->get();
        if (empty($this->routeMap)) {
            $this->routeMap = [
                'https' => false,
                'NAME_CONTROLLER' => 'page',
                'NAME_METHOD' => 'login',
                'publicWeb' => '',

                'routes' => [
                    'default' => [
                        '[task]/[action]/[params]',
                        'task=[task]&action=[action]',
                        'params' => '(.*)',
                        '_params' => [
                            '[name]/[value]/',
                            '[name]=[value]',
                        ],
                    ],
                ],
            ]; // For url
        }

        $this->routeMapParse = $this->routerConfig->get('routes', $this->routeMap['routes']); // For parsing array

        // Check forced HTTPS
        if ($this->https === true) {
            $this->requestPrefix = 'https://';
            // If forced than redirect
            if (isset($_SERVER['REQUEST_SCHEME']) and ((!empty($_SERVER['REQUEST_SCHEME']) and $_SERVER['REQUEST_SCHEME'] === 'http'))) {
                return Response::create()->headers(
                    [
                        'Refresh' => $this->requestPrefix . $this->domain . '/' . $_SERVER['REQUEST_URI'],
                    ]
                )->display();
            }
        } else {
            $this->requestPrefix = 'http://';
            if ((isset($_SERVER['REQUEST_SCHEME']) and (!empty($_SERVER['REQUEST_SCHEME']) and ($_SERVER['REQUEST_SCHEME'] === 'https') or !empty($_SERVER['HTTPS']) and $_SERVER['HTTPS'] === 'on') or (!empty($_SERVER['SERVER_PORT']) and $_SERVER['SERVER_PORT'] === '443'))) {
                $this->requestPrefix = 'https://';
            }
        }

        return null;
    }

    /**
     * Set up http/https
     *
     * @param bool $option
     *
     * @return $this
     */
    public function setHttps($option = false)
    {
        if (!in_array($option, [true, false])) {
            throw new InvalidArgumentException('Incorrect option', 403);
        }

        if ($option === true) {
            $this->requestPrefix = 'https://';
        } else {
            $this->requestPrefix = 'http://';
            if ((isset($_SERVER['REQUEST_SCHEME']) and (!empty($_SERVER['REQUEST_SCHEME']) and ($_SERVER['REQUEST_SCHEME'] === 'https') or !empty($_SERVER['HTTPS']) and $_SERVER['HTTPS'] === 'on') or (!empty($_SERVER['SERVER_PORT']) and $_SERVER['SERVER_PORT'] === '443'))) {
                $this->requestPrefix = 'https://';
            }
        }

        $this->https = $option;

        return $this;
    }

    /**
     * Redirect.
     *
     * @param string $url The URI
     * @param int    $status
     *
     * @return Response|object
     */
    public static function redirect($url = '', $status = 301)
    {
        return Response::redirect($url, $status);
    }

    /**
     * __construct Class
     *
     * @param $app
     *
     * @return $this
     */
    public function boot($app)
    {
        $this->app = $app;

        $routerConfig = $this->app->config['router'] ?? [];
        $this->routeMap['routes'] = array_merge($this->routeMap['routes'] ?? [], $routerConfig['routes'] ?? []);
        $this->routeMapParse = array_merge($routerConfig['routes'] ?? [], $this->routeMapParse ?? []);

        $annotationRoute = $this->routerConfig->get('annotation', false);
        if ($annotationRoute === true) {
            if (PHP_SAPI !== 'cli') {
                $routesFile = 'routes.php';
                $controllersFile = 'controllers.php';
                $usedControllers = [];
                $controllerDirs = [APP_DIR . 'Controller/'];
                $cacheDir = APP_DIR . 'View/cache/';

                if (!is_array($controllerDirs)) {
                    throw new InvalidArgumentException('Controllers directory must be either string or array');
                }

                $this->controllerDirs = [];
                foreach ($controllerDirs as $d) {
                    $realPath = realpath($d);
                    if ($realPath !== false) {
                        $this->controllerDirs[] = $realPath;
                    }
                }

                // We save the cache dir
                if (!is_dir($cacheDir)) {
                    if (!mkdir($cacheDir, 0777, true)) {
                        throw new RuntimeException('Can\'t create cache directory');
                    }
                }

                if (!is_writable($cacheDir)) {
                    throw new RuntimeException('Cache directory must be writable by web server');
                }

                $this->cacheDir = rtrim($cacheDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
                $this->generateRoutes();

                $routesConfig = Config::load('routes', APP_DIR . 'View/cache/')->get();
                if (!empty($routesConfig)) {
                    $this->routeMapParse = array_merge($routesConfig, $this->routeMapParse);
                    $this->routeMap['routes'] = array_merge($routesConfig, $this->routeMap['routes']);
                }
            }
        }

        return $this;
    }

    /**
     * Annotations parser.
     */
    protected function generateRoutes()
    {
        $parsingNeeded = !file_exists($this->cacheDir . $this->routesFile);
        // We look for controller files
        $files = $this->findControllerFiles();

        // We check if there has been modifications since last cache generation
        if (!$parsingNeeded) {
            $routesCacheMtime = filemtime($this->cacheDir . $this->routesFile);
            foreach ($files as $file => $mtime) {
                if ($mtime > $routesCacheMtime) {
                    $parsingNeeded = true;
                    break;
                }
            }
        }

        // We look for deleted controller files
        if (!$parsingNeeded and file_exists($this->cacheDir . $this->controllersFile)) {
            include_once $this->cacheDir . $this->controllersFile;
            foreach ($this->usedControllers as $controllerFile) {
                if (!file_exists($controllerFile)) {
                    $parsingNeeded = true;
                    break;
                }
            }
        }

        $routes = [];
        foreach ($files as $file => $mtime) {
            $parseFile = $this->parseFile($file);
            if (!empty($parseFile)) {
                $routes = array_merge($routes, $parseFile);
            }
        }

        if (!empty($routes)) {
            usort(
                $routes,
                function ($a, $b) {
                    if (strlen($a['routePath']) === strlen($b['routePath'])) {
                        return 0;
                    }
                    return strcmp($b['routePath'],
                        $a['routePath']) ?: strlen($b['routePath']) - strlen($a['routePath']);
                }
            );

            // We regenerate cache file if needed
            if ($parsingNeeded) {
                $controllerFiles = [];
                $commonFileContent = '<?php' . "\r\n" . '/**' . "\r\n" . ' * annotations router %s cache file, create ' . date('c') . "\r\n" . ' */' . "\r\n\r\n";
                $routesFileContent = sprintf($commonFileContent, 'routes');
                $controllersFileContent = sprintf($commonFileContent, 'controllers');
                $routesFileContent .= 'return [';

                foreach ($routes as $key => $route) {
                    $routesFileContent .= "\r\n";
                    $routesFileContent .= "    '" . $route['routeName'] . "' => [" . "\r\n";
                    $routesFileContent .= "        '" . $route['routePath'] . "'," . "\r\n";
                    $routesFileContent .= "        'task=" . $route['task'] . "&action=" . $route['action'] . $route['substring'] . "'," . "\r\n";
                    $routesFileContent .= "    ]," . "\r\n";
                }

                $routesFileContent = rtrim($routesFileContent, ',' . "\r\n");
                $routesFileContent .= "\r\n" . "];";
                file_put_contents($this->cacheDir . $this->routesFile, $routesFileContent);
                $usedControllers = (count($controllerFiles) > 0) ? '$this->usedControllers = [\'' . implode('\',\'',
                        $controllerFiles) . '\'];' : '';
                file_put_contents($this->cacheDir . $this->controllersFile, $controllersFileContent . $usedControllers);
            }
        }
    }

    /**
     * Find all file in controller dir.
     */
    protected function findControllerFiles()
    {
        $result = [];
        foreach ($this->controllerDirs as $dir) {
            $directoryIterator = new \RecursiveDirectoryIterator($dir);
            $iterator = new \RecursiveIteratorIterator($directoryIterator);
            $files = new \RegexIterator($iterator, '/\.php$/i', \RecursiveRegexIterator::GET_MATCH);
            foreach ($files as $k => $v) {
                $result[$k] = filemtime($k);
            }
        }

        return $result;
    }

    /**
     * Parsing annotations
     *
     * @param string $file
     *
     * @return string|array
     */
    protected function parseFile($file)
    {
        $result = '';
        $routes = [];

        //Windows
        $appDir = str_replace('web/../app/', '', APP_DIR);
        //All
        $appDir = str_replace('web' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . '',
            '', APP_DIR);

        $task = str_replace($appDir . 'app' . DIRECTORY_SEPARATOR . 'Controller' . DIRECTORY_SEPARATOR . '', '', $file);
        $task = rtrim($task, '.php');
        $task = str_replace(DIRECTORY_SEPARATOR, ',', $task);

        // We load file content
        $content = file_get_contents($file);
        // We search for namespace
        $namespace = null;

        if (preg_match('/namespace\s+([\w\\\_-]+)/', $content, $matches) === 1) {
            $namespace = $matches[1];
        }

        // We look for class name
        if (preg_match('/class\s+([\w_-]+)/', $content, $matches) === 1) {
            $className = ($namespace !== null) ? $namespace . '\\' . $matches[1] : $matches[1];
            // We find class info's
            $path = str_replace('Controller.php', '.php', $className . '.php');
            $path = APP_DIR . str_replace('\\', '/', $path);
            if (is_file($path)) {
                include_once $path;
            }
            $reflector = new \ReflectionClass($className);
            $prefix = '';
            if (preg_match('/@RoutePrefix\(["\'](((?!(["\'])).)*)["\']\)/', $reflector->getDocComment(),
                    $matches) === 1) {
                $prefix = $matches[1];
            }
            $methods = $reflector->getMethods(\ReflectionMethod::IS_PUBLIC);
            $result = '';

            $vars = null;
            foreach ($methods as $m) {
                $vars = null;
                if ($m->isStatic()) {
                    continue;
                }

                if (preg_match('/@Route\(\s*(.*)*\)/', $m->getDocComment(), $matches) === 1) {
                    preg_match_all('/(?![(@Route()])([@a-zA-Z0-9"[\]:_> \'(.*)\/[=])+["]/', $matches[0], $route2);
                    $routeName = null;
                    if (preg_match('/name=["](.*)["]/', $route2[0][1], $matches)) {
                        $routeName = $matches[1];
                    }
                    if (empty($routeName)) {
                        throw new InvalidArgumentException('Incorrect name', 403);
                    }

                    $routePath = trim($route2[0][0], '"');
                    $routePath = trim($routePath);
                    $routePath = ltrim($routePath, '/');
                    $lChar = substr($routePath, -1);
                    if ($lChar == ']') {
                        $routePath = $routePath . '/';
                    }
                    preg_match_all('!\[(.+?)\]!i', $routePath, $expression_);
                    $iCount = count($expression_[0]);
                    for ($i = 0; $i < $iCount; $i++) {
                        if ($expression_[0][$i] != '[params]') {
                            $vars .= '&' . $expression_[1][$i] . '=' . $expression_[0][$i];
                        }
                    }
                    $routes[$routePath] = [
                        'routeName' => $routeName,
                        'routePath' => $routePath,
                        'task' => $task,
                        'action' => $m->name,
                        'substring' => $vars,
                    ];
                }
            }

            return $routes;
        }

        return null;
    }

    /**
     * @return array|string[]
     */
    public function getRoutes()
    {
        return $this->routeMap;
    }

    /**
     * @param $routes
     */
    public function setRoutes($routes)
    {
        $this->routeMap = array_merge($this->routeMap, $routes);
    }

    /**
     * Check current active page
     *
     * @param string $url
     *
     * @return bool
     */
    public function isActive(string $url)
    {
        if ($this->makeUrl($url, true) === str_replace($this->uri, '', $_SERVER['REQUEST_URI'])) {
            return true;
        }

        return false;
    }

    /**
     * Generate url
     *
     * @param string|null $url
     * @param string|bool $onlyExt
     *
     * @return null|string
     */
    public function makeUrl(string $url = null, $onlyExt = false)
    {
        $paramsHook = explode('#', $url);
        $params = explode('?', $paramsHook[0]);
        $params_ = explode('/', $params[0]);
        $task = $params_[0];
        $action = null;

        if (isset($params_[1]) and !empty($params_[1])) {
            $action = $params_[1];
        }

        if (isset($params[1])) {
            parse_str($params[1], $params);
        } else {
            $params = [];
        }

        $findKey = explode('?', $url);
        if (isset($findKey[0])) {
            $findKey = $findKey[0];
        }

        if (defined('MOD_REWRITE') and MOD_REWRITE === true) {
            if (isset($this->routeMap['routes'][$findKey])) {
                $expressionUrl = $this->routeMap['routes'][$findKey][0];
                foreach ($params as $key => $value) {
                    $expressionUrl = str_replace('[' . $key . ']', $value, $expressionUrl, $count);
                    if ($count > 0) {
                        unset($params[$key]);
                    }
                }

                if (isset($params)) {
                    if (isset($this->routeMap['routes'][$findKey]['_params'])) {
                        $expressionUrl = str_replace('[params]',
                            $this->parseParams($this->routeMap['routes'][$findKey]['_params'][0], $params),
                            $expressionUrl);
                    } elseif (!empty($params)) {
                        $expressionUrl = $expressionUrl . '?' . http_build_query($params);
                    }
                }
            } else {
                $expressionUrl = $this->routeMap['routes']['default'][0];
                $expressionUrl = str_replace('[task]', $task, $expressionUrl);
                $expressionUrl = str_replace('[action]', $action, $expressionUrl);
                if (isset($params)) {
                    $expressionUrl = str_replace('[params]',
                        $this->parseParams($this->routeMap['routes']['default']['_params'][0], $params),
                        $expressionUrl);
                }
            }
        } else {
            if (empty($task)) {
                $expressionUrl = '';
            } else {
                if (isset($this->routeMap['routes'][$findKey])) {
                    $expressionUrl0 = $this->routeMap['routes'][$findKey][1];
                    foreach ($params as $key => $value) {
                        $expressionUrl0 = str_replace('[' . $key . ']', $value, $expressionUrl0, $count);
                        if ($count > 0) {
                            unset($params[$key]);
                        }
                    }

                    $expressionUrl = $expressionUrl0;
                } else {
                    $expressionUrl = 'task=' . $task;
                    if (!empty($action)) {
                        $expressionUrl = 'task=' . $task . '&action=' . $action;
                    }
                }

                if (!empty($params)) {
                    if (!empty($expressionUrl)) {
                        $expressionUrl .= '&';
                    }
                    $expressionUrl = $expressionUrl . http_build_query($params);
                }
                $expressionUrl = 'index.php?' . $expressionUrl;
            }
        }

        $parsedUrl = \parse_url($this->domain);

        if (isset($parsedUrl['scheme'])) {
            $this->requestPrefix = $parsedUrl['scheme'] . '://';
            $this->domain = ltrim($this->domain, $parsedUrl['scheme'] . '://');
        }

        $HTTP_HOST = $this->domain;

        if (!empty($this->subdomain)) {
            $HTTP_HOST = $this->subdomain . '.' . $this->domain;
        }

        $url = null;
        if ($onlyExt === false) {
            $url = $this->requestPrefix . $HTTP_HOST . '/';
        }

        $url .= $expressionUrl;
        $url = rtrim($url, '/');

        unset($this->subdomain);
        $this->domain = HTTP_HOST;
        //$this->setHttps($this->routerConfig->get('https', false));

        return $url;
    }

    /**
     * Parse url params into a 'request'
     *
     * @param string $routing
     * @param array  $params
     *
     * @return string
     */
    protected function parseParams($routing, $params)
    {
        $return = null;

        foreach ($params as $key => $value) {
            $return .= str_replace(['[name]', '[value]'], [$key, $value], $routing);
        }

        return $return;
    }

    /**
     * @param null|string $url
     * @param null|string $path
     *
     * @return null|string
     */
    public function publicWeb($url = null, $path = null)
    {
        if (is_null($path)) {
            $path = $this->routeMap['publicWeb'];
        }

        $expressionUrl = $url;
        $url = $this->requestPrefix . $this->domain . '/' . $path;
        $url .= $expressionUrl;

        unset($this->subdomain);
        $this->domain = HTTP_HOST;
        //$this->setHttps($this->routerConfig->get('https', false));

        return $url;
    }

    /**
     * Parse request.
     */
    public function parseGets()
    {
        $request = preg_replace('!' . $this->uri . '(.*)$!i', '$1', $_SERVER['REQUEST_URI']);
        if (defined('MOD_REWRITE') and MOD_REWRITE === true) {
            if (substr($request, -1) != '/') {
                $request .= '/';
            }

            $parseUrl = $this->parseUrl($request);
            $this->namespace = $parseUrl['v']['namespace'] ?? '';
            parse_str($parseUrl['sVars'], $gets);

            $this->controller = !empty($gets['task']) ? $gets['task'] : $this->routeMap['NAME_CONTROLLER'];
            unset($gets['task']);

            $this->action = !empty($gets['action']) ? $gets['action'] : $this->routeMap['NAME_METHOD'];
            unset($gets['action']);
            $_GET = array_merge($_GET, $gets);
        } else {
            $this->controller = !empty($_GET['task']) ? $_GET['task'] : $this->routeMap['NAME_CONTROLLER'];
            $this->action = !empty($_GET['action']) ? $_GET['action'] : $this->routeMap['NAME_METHOD'];
        }

        $_GET['task'] = $this->controller;
        $_GET['action'] = $this->action;
    }

    /**
     * Match given request
     *
     * @param string      $request
     * @param string|null $routingParse
     *
     * @return string|array
     */
    protected function parseUrl($request, $routingParse = null)
    {
        $vars = null;
        $v = [];

        if ($routingParse === null) {
            $routingParse = $this->routeMapParse;
        }

        $pos = strpos($request, '?task=');
        if ($pos !== false) {
            $request = substr_replace($request, '/?task=', $pos, strlen('?task='));
        }

        $request = str_replace('?', '&', $request);

        foreach ($routingParse as $k => $v) {
            if (!is_array($v)) {
                continue;
            }

            preg_match_all('!\[(.+?)\]!i', $v[0], $expression_);
            $expressionMatch = preg_replace_callback(
                '!\[(.+?)\]!i',
                function ($m) use ($k) {
                    return $this->transformParam($m[1], $k);
                },
                $v[0]
            );

            if (preg_match_all('!' . $expressionMatch . '!i', $request, $expression__)) {
                $args = [];

                if (isset($v['args'])) {
                    $args = $v['args'];
                }

                foreach ($expression__ as $k_ => $v_) {
                    foreach ($v_ as $kkk => $vvv) {
                        if (!isset($expression_[1][$k_ - 1])) {
                            $expression_[1][$k_ - 1] = null;
                        }

                        if ($kkk > 0) {
                            $expression[] = [$expression_[1][$k_ - 1] . '_' . $kkk, $vvv];
                        } else {
                            $expression[] = [$expression_[1][$k_ - 1], $vvv];
                        }
                    }
                }
                unset($expression[0]);
                $iCount = count($expression__[0]);

                if ($iCount > 1) {
                    for ($i = 0; $i < $iCount; $i++) {
                        if ($i > 0) {
                            $vars .= '&' . preg_replace('!\[(.+?)\]!i', '[$1_' . $i . ']', $v[1]);
                        } else {
                            $vars = '&' . $v[1];
                        }
                    }
                } else {
                    $vars = '&' . $v[1];
                }

                foreach ($expression as $v_) {
                    if (!isset($v['_' . $v_[0]])) {
                        $v['_' . $v_[0]] = null;
                    }

                    if (!is_array($v['_' . $v_[0]])) {
                        foreach ($args as $key => $value) {
                            $args[$key] = str_replace('[' . $v_[0] . ']', $v_[1], $args[$key]);
                        }
                        $vars = str_replace('[' . $v_[0] . ']', $v_[1], $vars);
                    } else {
                        $vars = $vars . $this->parseUrl($v_[1], [$v['_' . $v_[0]]])['sVars'];
                    }
                }
                $this->parseArgs = $args;
                break;
            }
        }

        if (isset($this->app->debug)) {
            $this->app->debug->addHeader(['X-DF-Debug-sVars' => $vars]);
        }

        return ['v' => $v, 'sVars' => $vars];
    }

    /**
     * Prepares the regexp
     *
     * @param string $param
     * @param string $k
     *
     * @return string
     */
    protected function transformParam($param, $k)
    {
        if (isset($this->routeMapParse[$k][$param]) and !is_array($this->routeMapParse[$k][$param])) {
            return $this->routeMapParse[$k][$param];
        } else {
            return '(.+?)';
        }
    }

    /**
     * Return Current path
     *
     * @return string
     */
    public function currentPath()
    {
        $request = preg_replace('!' . $this->uri . '(.*)$!i', '$1', $_SERVER['REQUEST_URI']);
        if (defined('MOD_REWRITE') and MOD_REWRITE === true) {
            if (substr($request, -1) != '/') {
                $request .= '/';
            }

            $parseUrl = $this->parseUrl($request);
            $gets = $parseUrl['sVars'];
        } else {
            $gets = $_SERVER['QUERY_STRING'];
        }

        return $gets;
    }

    /**
     * Redirect delay.
     *
     * @param int $delay time in seconds
     *
     * @return $this
     */
    public function delay(int $delay)
    {
        $this->delay = $delay;

        return $this;
    }

    /**
     * Set up subdomain prefix.
     *
     * @param string $subdomain
     *
     * @return object
     */
    public function subdomain($subdomain)
    {
        $this->subdomain = $subdomain;

        return $this;
    }

    /**
     * Set up domain.
     *
     * @param string $domain
     *
     * @return object
     */
    public function domain($domain)
    {
        $this->domain = $domain;

        return $this;
    }

    /**
     * Set up new route.
     *
     * @param array $newRoute
     */
    public function addRoute($newRoute)
    {
        $this->routeMap['routes'] = array_merge($this->routeMap['routes'], $newRoute);
        $this->routeMapParse = array_merge($this->routeMapParse, $newRoute);
    }

    /**
     * @return Response
     */
    public function response()
    {
        return new Response();
    }
}


<?php

/**
 * DframeFramework
 * Copyright (c) Sławomir Kaleta.
 *
 * @license https://github.com/dframe/dframe/blob/master/LICENCE (MIT)
 */

namespace Dframe;

/**
 * Model Class.
 *
 * @author Sławomir Kaleta <slaszka@gmail.com>
 */
abstract class Model extends Loader
{
    /**
     * Standard method for returning  result from the method.
     *
     * @param array $errors
     *
     * @return array
     */
    public function methodFail($errors = null)
    {
        if ($errors === null) {
            return $this->methodResult(false);
        }

        if (!is_array($errors)) {
            $errors = [$errors];
        }

        return $this->methodResult(false, ['errors' => $errors]);
    }

    /**
     * Standard method for returning the result from the method.
     *
     * @param bool  $type
     * @param array $array
     *
     * @return array
     */
    public function methodResult($type, $array = null)
    {
        if (!is_null($array)) {
            return array_merge(['return' => $type], $array);
        }

        return ['return' => $type];
    }

    /**
     * Init method.
     */
    public function init()
    {
    }
}


<?php

/**
 * DframeFramework
 * Copyright (c) Sławomir Kaleta.
 *
 * @license https://github.com/dframe/dframe/blob/master/LICENCE (MIT)
 */

namespace Dframe;

/**
 * Debug Class.
 *
 * @author Sławomir Kaleta <slaszka@gmail.com>
 */
class Debug
{
    /**
     * @var array
     */
    protected $headers = [];

    /**
     * @param $headers
     *
     * @return $this
     */
    public function addHeader($headers)
    {
        $this->headers = array_unique(array_merge($this->headers, $headers));

        return $this;
    }

    /**
     * @return array
     */
    public function getHeader()
    {
        return $this->headers;
    }
}


<?php

/**
 * DframeFramework
 * Copyright (c) Sławomir Kaleta.
 *
 * @license https://github.com/dframe/dframe/blob/master/LICENCE (MIT)
 */

namespace Dframe;

use Dframe\Loader\Exceptions\LoaderException;
use Dframe\Router\Response;

/**
 * Loader Class.
 *
 * @author Sławomir Kaleta <slaszka@gmail.com>
 */
class Loader
{
    /**
     * @var \Dframe\Router
     */
    public $router;

    /**
     * @var \Bootstrap|null
     */
    public $baseClass;

    /**
     * @var string
     */
    protected $fileExtension = '.php';

    /**
     * @var string
     */
    protected $namespaceSeparator = '\\';

    /**
     * Loader constructor.
     *
     * @param null|object|\Bootstrap $bootstrap
     *
     * @throws LoaderException
     */
    public function __construct($bootstrap = null)
    {
        spl_autoload_register([$this, 'autoload']);

        if (!defined('APP_DIR')) {
            throw new LoaderException('Please Define APP_DIR in Main config.php', 500);
        }
        if (!defined('SALT')) {
            throw new LoaderException('Please Define SALT in Main config.php', 500);
        }

        $this->baseClass = empty($bootstrap) ? new \Bootstrap() : $bootstrap;

        $baseClass = new \Bootstrap();
        foreach ($baseClass->providers['core'] ?? [] as $key => $value) {
            $this->$key = new $value($this);
            if (method_exists($this->$key, 'boot') or is_callable([$this->$key, 'boot'])) {
                $this->$key->boot($this);
            }
        }

        if (is_null($bootstrap)) {
            foreach ($baseClass->providers['baseClass'] ?? [] as $key => $value) {
                $this->baseClass->$key = new $value($this->baseClass);
            }

            $this->baseClass->modules = (object)[];
            foreach ($baseClass->providers['modules'] ?? [] as $key => $value) {
                $this->baseClass->modules->$key = new $value($this);
                $this->baseClass->modules->$key->register();
                $this->baseClass->modules->$key->boot($this->baseClass->modules->$key->app);
            }
        } else {
            foreach ($this->baseClass->providers['modules'] ?? [] as $key => $value) {
                foreach ($baseClass->providers['core'] ?? [] as $key2 => $value2) {
                    if (method_exists($this->$key2, 'boot') or is_callable([$this->$key2, 'boot'])) {
                        $this->$key2->boot($this->baseClass->modules->$key->app);
                    }
                }
            }
        }

        return $this;
    }

    /**
     * @param $class
     *
     * @return bool|mixed
     * @throws LoaderException
     */
    public static function autoload($class)
    {
        if (substr($class, -4) == "View") {
            $class = substr($class, 0, -4);
        } elseif (substr($class, -5) == "Model") {
            $class = substr($class, 0, -5);
        } elseif (substr($class, -10) == "Controller") {
            $class = substr($class, 0, -10);
        } else {
            return false;
        }

        $directory = explode('/', str_replace('\\', '/', ltrim($class, '\\')));

        $class = array_pop($directory);
        $directory = array_merge($directory, explode('/', str_replace('_', '/', $class)));
        $class = array_pop($directory);
        $directory = rtrim(APP_DIR . join('/', $directory), '/');

        if (!empty($class)) {
            if (is_file($path = $directory . '/' . $class . '.php')) {
                return require_once $path;
            }

            throw new LoaderException('Couldn\'t locate ' . $class . '' . implode(', ', func_get_args()));
        }
    }

    /**
     * Model Loader
     *
     * @param string      $name
     * @param null|string $namespace
     *
     * @return object
     */
    public function loadModel($name, $namespace = null)
    {
        return $this->loadObject($name, 'Model', $namespace);
    }

    /**
     * Loading files
     *
     * @param string      $name
     * @param string      $type
     * @param null|string $namespace
     *
     * @return object|bool
     */
    private function loadObject($name, $type, $namespace = null)
    {
        try {
            if (!$this->isCamelCaps($name, true)) {
                if (!defined('CODING_STYLE') or (defined('CODING_STYLE') and CODING_STYLE === true)) {
                    throw new LoaderException('Camel Sensitive is on. Can not use ' . $type . ' ' . $name . ' try to use StudlyCaps or CamelCase');
                }
            }

            if (!in_array($type, (['Model', 'View']))) {
                return false;
            }

            if (!empty($namespace)) {
                $name = '\\' . $namespace . '\\' . $type . '\\' . $name;
            } else {
                $name = $namespace . '\\' . $type . '\\' . $name . $type;
            }

            $name = str_replace(DIRECTORY_SEPARATOR, $this->namespaceSeparator, $name);
            $name = str_replace('/', $this->namespaceSeparator, $name);

            $ob = new $name($this->baseClass);
            if (method_exists($ob, 'start')) {
                $ob->start();
            }
            if (method_exists($ob, 'init')) {
                $ob->init();
            }
        } catch (LoaderException $e) {
            if (ini_get('display_errors') === "on") {
                $msg = null;
                $msg .= '
';
                $msg .= 'Message: ' . $e->getMessage() . '

';

                $msg .= 'Accept: ' . $_SERVER['HTTP_ACCEPT'] . '
';
                if (isset($_SERVER['HTTP_REFERER'])) {
                    $msg .= 'Referer: ' . $_SERVER['HTTP_REFERER'] . '

';
                }

                $msg .= 'Request Method: ' . $_SERVER['REQUEST_METHOD'] . '

';
                $msg .= 'Current file Path: ' . $this->router->currentPath() . '
';
                $msg .= 'File Exception: ' . $e->getFile() . ':' . $e->getLine() . '

';
                $msg .= 'Trace:
' . $e->getTraceAsString() . '
';
                $msg .= '
';

                return Response::create($msg)->display();
            }

            $routes = Config::load('router')->get('routes');
            if (!empty($routes['error/:code'])) {
                return Response::redirect('error/:code?code=400', 400)->display();
            }

            return Response::create()->status(500)->display();
        }

        return $ob;
    }

    /**
     * @param string $string
     * @param bool   $classFormat
     * @param bool   $public
     * @param bool   $strict
     *
     * @return bool
     */
    public static function isCamelCaps($string, $classFormat = false, $public = true, $strict = true)
    {

        // Check the first character first.
        if ($classFormat === false) {
            $legalFirstChar = '';
            if ($public === false) {
                $legalFirstChar = '[_]';
            }

            if ($strict === false) {
                // Can either start with a lowercase letter,
                // or multiple uppercase
                // in a row, representing an acronym.
                $legalFirstChar .= '([A-Z]{2,}|[a-z])';
            } else {
                $legalFirstChar .= '[a-z]';
            }
        } else {
            $legalFirstChar = '[A-Z]';
        }

        if (preg_match("/^$legalFirstChar/", $string) === 0) {
            return false;
        }

        // Check that the name only contains legal characters.
        $legalChars = '[^a-zA-Z0-9\/]';
        if (preg_match("|$legalChars|", substr($string, 1)) > 0) {
            return false;
        }

        if ($strict === true) {
            // Check that there are not two capital letters
            // next to each other.
            $length = strlen($string);
            $lastCharWasCaps = $classFormat;

            for ($i = 1; $i < $length; $i++) {
                $ascii = ord($string[$i]);

                if (($ascii >= 48 and $ascii <= 57) or $ascii === 47) {
                    // The character is a number, so it cant be a capital.
                    $isCaps = false;
                } else {
                    if (strtoupper($string[$i]) === $string[$i]) {
                        $isCaps = true;
                    } else {
                        $isCaps = false;
                    }
                }

                if ($isCaps === true and $lastCharWasCaps === true) {
                    return false;
                }

                $lastCharWasCaps = $isCaps;
            }
        }//end if

        return true;
    }

    /**
     * View Loader
     *
     * @param string      $name
     * @param null|string $namespace
     *
     * @return object
     */
    public function loadView($name, $namespace = null)
    {
        return $this->loadObject($name, 'View', $namespace);
    }

    /**
     * Establish the requested controller as an object.
     *
     * @param string      $controller
     * @param null|string $namespace
     *
     * @return mixed
     */
    public function loadController($controller, $namespace = null)
    {
        try {
            $subController = null;
            if (strstr($controller, ',') !== false) {
                $url = explode(',', $controller);
                $urlCount = count($url) - 1;
                $subController = '';

                for ($i = 0; $i < $urlCount; $i++) {
                    if (!defined('CODING_STYLE') or (defined('CODING_STYLE') and CODING_STYLE === true)) {
                        $subController .= ucfirst($url[$i]) . DIRECTORY_SEPARATOR;
                    } else {
                        $subController .= $url[$i] . DIRECTORY_SEPARATOR;
                    }
                }

                $controller = $url[$urlCount];
            }

            if (!defined('CODING_STYLE') or (defined('CODING_STYLE') and CODING_STYLE === true)) {
                $controller = ucfirst($controller);
            }

            $controller = str_replace(DIRECTORY_SEPARATOR, $this->namespaceSeparator, $controller);

            if (!empty($namespace) && $namespace == '\\') {
                $load = $controller;
            } elseif (!empty($namespace)) {
                $class = '\\' . $namespace . '\\Controller\\' . $subController . $controller;
                $load = str_replace('/', $this->namespaceSeparator, $class);
            } else {
                $load = $this->namespaceSeparator . 'Controller' . $this->namespaceSeparator . $subController . $controller . 'Controller';
                $load = str_replace(DIRECTORY_SEPARATOR, $this->namespaceSeparator, $load);
            }

            if (isset($this->debug)) {
                $this->debug->addHeader(['X-DF-Debug-Controller' => $load]);
            }

            $controller = new $load($this->baseClass);
        } catch (\Exception $e) {
            if (ini_get('display_errors') === 'on') {
                $msg = null;
                $msg .= '
';
                $msg .= 'Message: ' . $e->getMessage() . '

';

                $msg .= 'Accept: ' . $_SERVER['HTTP_ACCEPT'] . '
';
                if (isset($_SERVER['HTTP_REFERER'])) {
                    $msg .= 'Referer: ' . $_SERVER['HTTP_REFERER'] . '

';
                }

                $msg .= 'Request Method: ' . $_SERVER['REQUEST_METHOD'] . '

';
                $msg .= 'Current file Path: ' . $this->router->currentPath() . '
';
                $msg .= 'File Exception: ' . $e->getFile() . ':' . $e->getLine() . '

';
                $msg .= 'Trace:
' . $e->getTraceAsString() . '
';
                $msg .= '
';

                return Response::create($msg)->display();
            }

            $routes = Config::load('router')->get('routes');
            if (!empty($routes['error/:code'])) {
                return Response::redirect('error/:code?code=400', 400)->display();
            }

            return Response::create()->status(500)->display();
        }

        return $controller;
    }

    //end isCamelCaps()

    /**
     * Method init that works like __construct called at the beginning of the code.
     */
    public function init()
    {
    }

    /**
     * A method that works like __destruct called at the end of the code.
     */
    public function end()
    {
    }
}


<?php

/**
 * DframeFramework
 * Copyright (c) Sławomir Kaleta.
 *
 * @license https://github.com/dframe/dframe/blob/master/LICENCE (MIT)
 */

namespace Dframe;

use Psr\SimpleCache\CacheInterface;

/**
 * Token Class.
 *
 * @author Sławomir Kaleta <slaszka@gmail.com>
 */
class Token implements CacheInterface
{
    /**
     * @var CacheInterface
     */
    protected $driver;

    /**
     * @var array
     */
    protected $token = [];

    /**
     * @var array
     */
    protected $time = [];

    /**
     * constructor.
     *
     * @param string $driver
     */
    public function __construct($driver)
    {
        $this->driver = $driver;
        if (!($this->driver instanceof CacheInterface) === true) {
            throw new \Exception("This class Require instance Of Psr\SimpleCache\CacheInterface", 1);
        }

        $token = $this->driver->get('token');
        if (!empty($token)) {
            $this->token = $token;
        }

        $timeToken = $this->driver->get('timeToken');
        if (!empty($timeToken)) {
            $this->time = $timeToken;
        }
    }

    /**
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function clear()
    {
        $this->token = [];
        $this->time = [];

        $this->driver->set('token', $this->token);
        $this->driver->set('timeToken', $this->time);
    }

    /**
     * @param iterable $keys
     * @param null     $default
     *
     * @return iterable|void
     */
    public function getMultiple($keys, $default = null)
    {
        $cache = [];
        foreach ($keys as $key) {
            $cache[$key] = $this->get($key, $default);
        }

        return $cache;
    }

    /**
     * {@inheritdoc}
     */
    public function setMultiple($values, $ttl = null)
    {
        foreach ($values as $value) {
            $this->set($value['key'], $value['value'], $ttl);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function deleteMultiple($keys)
    {
        foreach ($keys as $key) {
            $this->delete($key);
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function has($key)
    {
        if (isset($this->token[$key]) and $this->getTime($key) >= time()) {
            return true;
        }

        return false;
    }

    /**
     * @param      $key
     * @param      $token
     * @param bool $delete
     *
     * @return bool
     */
    public function isValid($key, $token, $delete = false)
    {
        $getToken = $this->get($key);

        if ($delete === true) {
            $this->delete($key);
        }

        if ($getToken === $token) {
            return true;
        }

        return false;
    }

    /**
     * @param string      $key
     * @param null|string $default
     *
     * @return mixed
     */
    public function get($key, $default = null)
    {
        if (isset($this->token[$key]) and $this->getTime($key) >= time()) {
            return $this->token[$key];
        }

        return $this->generate($key)->token[$key];
    }

    /**
     * @param string $key
     *
     * @return mixed|null
     */
    public function getTime($key)
    {
        return isset($this->time[$key]) ? $this->time[$key] : null;
    }

    /**
     * @param string $key
     * @param        $time
     *
     * @return $this
     */
    public function setTime($key, $time)
    {
        if (isset($this->token[$key])) {
            $this->time[$key] = intval($time);
            $this->driver->set('timeToken', $this->time);
        }

        return $this;
    }

    /**
     * @param string $key
     *
     * @return $this
     */
    public function generate($key)
    {
        $this->set($key, md5(uniqid(rand(), true)));
        $this->setTime($key, time() + 3600);

        return $this;
    }

    /**
     * @param string $key
     * @param mixed  $value
     * @param null   $ttl
     *
     * @return $this
     */
    public function set($key, $value, $ttl = null)
    {
        $this->token[$key] = $value;
        $this->driver->set('token', $this->token);

        return $this;
    }

    /**
     * @param string $key
     */
    public function delete($key)
    {
        if (isset($this->token[$key])) {
            unset($this->token[$key]);
        }

        if (isset($this->time[$key])) {
            unset($this->time[$key]);
        }

        $this->driver->set('token', $this->token);
        $this->driver->set('timeToken', $this->time);
    }
}


<?php

namespace Airbrake\EventFilter\Error;

class NotifyOnWarning implements FilterInterface
{
    /** @var array  */
    protected static $warningErrors = array(
        \E_NOTICE            => 'Notice',
        \E_STRICT            => 'Strict',
        \E_USER_WARNING      => 'User Warning',
        \E_USER_NOTICE       => 'User Notice',
        \E_DEPRECATED        => 'Deprecated',
        \E_WARNING           => 'Warning',
        \E_USER_DEPRECATED   => 'User Deprecated',
        \E_CORE_WARNING      => 'Core Warning',
        \E_COMPILE_WARNING   => 'Compile Warning',
        \E_RECOVERABLE_ERROR => 'Recoverable Error'
    );

    /**
     * Filters out PHP errors before they get sent
     *
     * @param int $type
     * @param string $message
     * @param string $file
     * @param int $line
     * @param array $context
     * @see http://us3.php.net/manual/en/function.set-error-handler.php
     * @return bool
     */
    public function shouldSendError($type, $message, $file = null, $line = null, $context = null)
    {
        return !array_key_exists($type, self::$warningErrors);
    }
}


<?php
namespace Airbrake;

use Exception;
use Airbrake\EventFilter\Error\ErrorReporting as ErrorReporting;
use Airbrake\EventFilter\Error\NotifyOnWarning as NotifyOnWarning;
use Airbrake\EventFilter\Exception\AirbrakeExceptionFilter as AirbrakeExceptionFilter;

/**
 * Airbrake EventHandler class.
 *
 * @package    Airbrake
 * @author     Drew Butler <drew@dbtlr.com>
 * @copyright  (c) 2011-2013 Drew Butler
 * @license    http://www.opensource.org/licenses/mit-license.php
 */
class EventHandler
{
    /**
     * The singleton instance
     */
    protected static $instance = null;
    protected $airbrakeClient  = null;
    protected $notifyOnWarning = null;

    protected $onErrorFilters = array();
    protected $onExceptionFilters = array();

    protected $warningErrors = array(\E_NOTICE            => 'Notice',
                                     \E_STRICT            => 'Strict',
                                     \E_USER_WARNING      => 'User Warning',
                                     \E_USER_NOTICE       => 'User Notice',
                                     \E_DEPRECATED        => 'Deprecated',
                                     \E_WARNING           => 'Warning',
                                     \E_USER_DEPRECATED   => 'User Deprecated',
                                     \E_CORE_WARNING      => 'Core Warning',
                                     \E_COMPILE_WARNING   => 'Compile Warning',
                                     \E_RECOVERABLE_ERROR => 'Recoverable Error' );

    protected $fatalErrors = array(\E_ERROR             => 'Error',
                                   \E_PARSE             => 'Parse',
                                   \E_COMPILE_ERROR     => 'Compile Error',
                                   \E_CORE_ERROR        => 'Core Error',
                                   \E_USER_ERROR        => 'User Error' );

    /**
     * Build with the Airbrake client class.
     *
     * @param Client $client
     * @param bool $notifyOnWarning
     */
    public function __construct(Client $client, $notifyOnWarning = false)
    {
        if (!$notifyOnWarning) {
            $this->addErrorFilter(new NotifyOnWarning());
        }

        $this->airbrakeClient = $client;
    }

    /**
     * @return self
     */
    public static function getInstance()
    {
        return self::$instance;
    }

    /**
     * @return Client
     */
    public function getClient()
    {
        return $this->airbrakeClient;
    }

    /**
     * Get the current handler.
     *
     * @param string $apiKey
     * @param bool $notifyOnWarning
     * @param array $options
     * @return EventHandler
     */
    public static function start($apiKey, $notifyOnWarning = false, array $options = array())
    {
        if (!isset(self::$instance)) {
            $config = new Configuration($apiKey, $options);

            $client = new Client($config);
            self::$instance = new self($client, $notifyOnWarning);

            if (null !== $config->get('errorReportingLevel')) {
                self::$instance->addErrorFilter(new ErrorReporting($config));
            }

            self::$instance->addExceptionFilter(new AirbrakeExceptionFilter());

            set_error_handler(array(self::$instance, 'onError'));
            set_exception_handler(array(self::$instance, 'onException'));
            register_shutdown_function(array(self::$instance, 'onShutdown'));
        }

        return self::$instance;
    }

    /**
     * Add an error filter that is applied to any errors before posting them
     * to your airbrake server.
     * @see http://us3.php.net/manual/en/function.set-error-handler.php
     * @param EventFilter\Error\FilterInterface $filter
     * @return EventHandler
     */
    public function addErrorFilter(EventFilter\Error\FilterInterface $filter)
    {
        $this->onErrorFilters[] = $filter;
        return $this;
    }

    /**
     * Add an error filter that is applied to any exceptions before posting them
     * to your airbrake server.
     * @see http://us3.php.net/manual/en/function.set-exception-handler.php
     * @param EventFilter\Exception\FilterInterface $filter
     * @return EventHandler
     */
    public function addExceptionFilter(EventFilter\Exception\FilterInterface $filter)
    {
        $this->onExceptionFilters[] = $filter;
        return $this;
    }

    /**
     * Revert the handlers back to their original state.
     */
    public static function reset()
    {
        if (isset(self::$instance)) {
            restore_error_handler();
            restore_exception_handler();
        }

        self::$instance = null;
    }

    /**
     * Catches standard PHP style errors
     *
     * @throws Exception
     * @see http://us3.php.net/manual/en/function.set-error-handler.php
     * @param int $type
     * @param string $message
     * @param string $file
     * @param string $line
     * @param array $context
     * @return bool
     */
    public function onError($type, $message, $file = null, $line = null, $context = null)
    {
        // This will catch silenced @ function calls and keep them quiet.
        if (ini_get('error_reporting') == 0) {
            return true;
        }

        if (isset($this->fatalErrors[$type])) {
            throw new Exception($message);
        }

        if ($this->shouldNotifyError($type, $message, $file, $line, $context)) {
            // Make sure we pass in the current backtrace, minus this function call.
            $backtrace = debug_backtrace();
            array_shift($backtrace);

            $this->airbrakeClient->notifyOnError($message, $backtrace);
            return true;
        }

        return true;
    }

    /**
     * @param int $type
     * @param string $message
     * @param string $file
     * @param int $line
     * @param null $context
     * @return bool
     */
    private function shouldNotifyError($type, $message, $file, $line, $context = null)
    {
        foreach ($this->onErrorFilters as $filter) {
            /** @var \Airbrake\EventFilter\Error\FilterInterface $filter */
            if (!$filter->shouldSendError($type, $message, $file, $line, $context)) {
                return false;
            }
        }
        return true;
    }

    /**
     * @param \Exception $exception
     * @return bool
     */
    private function shouldNotifyException($exception)
    {
        foreach ($this->onExceptionFilters as $filter) {
            /** @var \Airbrake\EventFilter\Exception\FilterInterface $filter */
            if (!$filter->shouldSendException($exception)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Catches uncaught exceptions.
     *
     * @see http://us3.php.net/manual/en/function.set-exception-handler.php
     * @param \Exception $exception
     * @return bool
     */
    public function onException(\Exception $exception)
    {
        if ($this->shouldNotifyException($exception)) {
            $this->airbrakeClient->notifyOnException($exception);
        }

        return true;
    }

    /**
     * Handles the PHP shutdown event.
     *
     * This event exists almost soley to provide a means to catch and log errors that might have been
     * otherwise lost when PHP decided to die unexpectedly.
     */
    public function onShutdown()
    {
        // If the instance was unset, then we shouldn't run.
        if (self::$instance == null) {
            return;
        }

        // This will help prevent multiple calls to this, incase the shutdown handler was declared
        // multiple times. This only should occur in unit tests, when the handlers are created
        // and removed repeatedly. As we cannot remove shutdown handlers, this prevents us from
        // calling it 1000 times at the end.
        self::$instance = null;

        // Get the last error if there was one, if not, let's get out of here.
        if (!$error = error_get_last()) {
            return;
        }

        // Don't notify on warning if not configured to.
        if (!$this->shouldNotifyError($error['type'], $error['message'], $error['file'], $error['line'])) {
            return;
        }

        // Build a fake backtrace, so we at least can show where we came from.
        $backtrace = array(
            array(
                'file' => $error['file'],
                'line' => $error['line'],
                'function' => '',
                'args' => array(),
            )
        );

        $this->airbrakeClient->notifyOnError('[Improper Shutdown] '.$error['message'], $backtrace);
    }
}


<?php
namespace Airbrake;

use ArrayAccess;
use IteratorAggregate;
use ArrayIterator;

/**
 * A record abstract that can help accelerate building models.
 *
 * The extended object simply needs to define what the properties of the object
 * are going to be. An example of this is as follows:
 *
 * <pre>
 * class Person extends Record
 * {
 *     protected $dataStore = array(
 *         'FirstName' => 'Harry',
 *         'LastName'  => 'Potter',
 *     );
 * }
 * </pre>
 *
 * Now you can simply retrieve these properties by requesting them by their key name.
 * So, if you were to call ->get('FirstName') it would
 * retrieve that key for you. Similarly, you can call set('FirstName', 'Drew') and
 * it will set that key. Give load() an array or stdClass of key value pairs and it
 * will parse those into their matching keys. Any key that is given that does not
 * exist in the parameters will be ignored.
 *
 * These objects may also be accessed and iterated over as if they were arrays. This means
 * that if you prefer the $obj['key'] syntax, you are free to use it. Any keys that are set
 * to it that are not known to the record type will be ignored and any that do not exist
 * when getting, will simply return null.
 *
 * @package    Airbrake
 * @author     Drew Butler <drew@dbtlr.com>
 * @copyright  (c) 2011-2013 Drew Butler
 * @license    http://www.opensource.org/licenses/mit-license.php
 */
abstract class Record implements ArrayAccess, IteratorAggregate
{
    protected $dataStore = array();

    /**
     * Load the given data array to the record.
     *
     * @param array|\stdClass $data
     */
    public function __construct($data = array())
    {
        $this->load($data);
        $this->initialize();
    }

    /**
     * Get the value for the given key.
     *
     * The given key should match one of the parameters about, but with out the
     * prefix. That is added on during this process.
     *
     * @param string $key
     * @return mixed
     */
    public function get($key)
    {
        if ($this->exists($key)) {
            return $this->dataStore[$key];
        }

        return null;
    }

    /**
     * Magic alias for the get() method.
     *
     * @param string $key
     * @return mixed
     */
    public function __get($key)
    {
        return $this->get($key);
    }

    /**
     * Set the given value to the given key.
     *
     * The given key should match one of the parameters about, but with out the
     * prefix. That is added on during this process.
     *
     * @param string $key
     * @param mixed $value
     */
    public function set($key, $value)
    {
        if ($this->exists($key)) {
            $this->dataStore[$key] = $value;
        }
    }

    /**
     * Magic alias for the set() method.
     *
     * @param string $key
     * @param mixed $value
     */
    public function __set($key, $value)
    {
        $this->set($key, $value);
    }

    /**
     * Load the given data array to the record.
     *
     * @param array|\stdClass $data
     */
    public function load($data)
    {
        if (!is_array($data) && !$data instanceof \stdClass) {
            return;
        }

        foreach ($data as $key => $value) {
            $this->set($key, $value);
        }
    }

    /**
     * Dump the data into an array.
     *
     * @return array
     */
    public function toArray()
    {
        return $this->dataStore;
    }

    /**
     * Is the given key set in this record?
     *
     * @param string $key
     * @return bool
     */
    public function exists($key)
    {
        return array_key_exists($key, $this->dataStore);
    }

    /**
     * Get the keys that are contained in this record.
     *
     * @return array
     */
    public function getKeys()
    {
        return array_keys($this->dataStore);
    }

    /**
     * Set the given value for the given key
     *
     * Part of the ArrayAccess interface.
     *
     * @param string $key
     * @param mixed $value
     */
    public function offsetSet($key, $value)
    {
        if (!is_null($key)) {
            $this->set($key, $value);
        }
    }

    /**
     * Is the given key available?
     *
     * Part of the ArrayAccess interface.
     *
     * @param string $key
     * @return bool
     */
    public function offsetExists($key)
    {
        return $this->exists($key);
    }

    /**
     * Set the given key to null
     *
     * Part of the ArrayAccess interface.
     *
     * @param string $key
     */
    public function offsetUnset($key)
    {
        $this->set($key, null);
    }

    /**
     * Get the value for the given key.
     *
     * Part of the ArrayAccess interface.
     *
     * @param string $key
     * @return mixed
     */
    public function offsetGet($key)
    {
        return $this->get($key);
    }

    /**
     * Get the iterator for the IteratorAggregate interface.
     *
     * @return ArrayIterator
     */
    public function getIterator()
    {
        return new ArrayIterator($this->dataStore);
    }

    /**
     * Optional method to declare that will initialize the data on construct.
     */
    protected function initialize()
    {

    }
}


<?php
/*
 * This file is part of Numbers.
 *
 * (c) 2013 Nicolò Martini
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Numbers;

/**
 * Class Number
 */
class Number
{
    /**
     * {@link http://php.net/manual/en/language.types.float.php}
     * @var int
     */
    public static $max_significants = 14;

    private $number;
    private $significants;

    /**
     * @param int|float $number
     * @return static
     */
    public static function n($number)
    {
        return new static($number);
    }

    /**
     * @param $number
     */
    public function __construct($number)
    {
        $this->number = $number;
        $this->significants = static::$max_significants;
    }

    /**
     * @param int $significants
     * @param bool $preserveInts
     * @return $this
     */
    public function round($significants, $preserveInts = false)
    {
        $this->significants = $significants;
        $decimals = $this->decimalsForPrecision($preserveInts);

        $this->number = round($this->number, $decimals);

        return $this;
    }

    /**
     * @return $this
     */
    public function floor()
    {
        $this->number = floor($this->number);

        return $this;
    }

    /**
     * @return $this
     */
    public function ceil()
    {
        $this->number = ceil($this->number);

        return $this;
    }

    /**
     * @return mixed
     */
    public function get()
    {
        return $this->number;
    }

    /**
     * Apply a function to the underlying number
     *
     * @param callable $callable
     * @return $this
     */
    public function apply($callable)
    {
        $this->number = call_user_func($callable, $this->number);

        return $this;
    }

    /**
     * Return the order of getMagnitude of the number
     * @return int
     */
    public function getMagnitude()
    {
        if ($this->number == 0)
            return 0;

        return floor(log10(abs($this->number)));
    }

    /**
     * Returns the n-th digit of the number in base $n, where n is the exponent of
     * $base in the expansion of $this->number in base $base
     *
     * As usual keep in mind that float precision is limited, so there can be errors
     * when the digit is of a magnitude 12-13 times smaller than the magnitude of
     * the numnber.
     *
     * @param int $n
     * @param int $base
     * @return float
     */
    public function getDigit($n, $base = 10)
    {
        $m = abs($this->number) / pow($base, $n);
        return $m % $base;
    }

    /**
     * @return int
     */
    public function getSign()
    {
        if ($this->number > 0)
            return 1;
        if ($this->number < 0)
            return -1;

        return 0;
    }

    /**
     * @return SciNotation
     */
    public function getSciNotation()
    {
        $magnitude = $this->getMagnitude();
        return new SciNotation(pow(10, -$magnitude) * $this->number, $magnitude);
    }

    /**
     * @param int $magnitude
     *
     * @return SuffixNotation
     */
    public function getSuffixNotation($magnitude = null)
    {
        $magnitude = isset($magnitude) ? $magnitude : $this->getMagnitude();
        $exp = 3 * floor($magnitude/3);

        return new SuffixNotation(pow(10, -$exp) * $this->number, new MagnitudeSuffix($magnitude));
    }

    /**
     * @param string $decPoint
     * @param string $separator
     * @return string
     */
    public function format($decPoint = '.', $separator = ',')
    {
        $decimals = $this->decimalsForPrecision();
        $string = number_format(round($this->number, $decimals), $decimals, $decPoint, $separator);

        $string = rtrim(rtrim($string, '0'), $decPoint);

        return $string;
    }

    /**
     * This is the same of format(), only that it will fallback to machine locale values
     * when some argument is missing.
     *
     * @param string|null $decPoint
     * @param string|null $separator
     * @return string
     */
    public function localeFormat($decPoint = null, $separator = null)
    {
        if (!isset($decPoint) || !isset($separator)) {
            $locale = localeconv();

            if (!isset($decPoint))
                $decPoint = $locale["decimal_point"];

            if (!isset($separator))
                $separator = $locale["thousands_sep"];
        }

        return $this->format($decPoint, $separator);
    }

    /**
     * @param bool $preserveInts
     * @return int|mixed
     */
    private function decimalsForPrecision($preserveInts = true)
    {
        $decimals = $this->significants - $this->getMagnitude() - 1;
        if ($preserveInts)
            $decimals = max(0, $decimals);

        return $decimals;
    }
}


<?php
namespace Airbrake;

use Exception;
use Airbrake\Connection\ConnectionInterface;

/**
 * Airbrake client class.
 *
 * @package    Airbrake
 * @author     Drew Butler <drew@dbtlr.com>
 * @copyright  (c) 2011-2013 Drew Butler
 * @license    http://www.opensource.org/licenses/mit-license.php
 */
class Client
{
    /** @var Configuration|null  */
    protected $configuration = null;

    /** @var Connection|null  */
    protected $connection = null;

    /** @var null */
    protected $notice = null;

    /**
     * Build the Client with the Airbrake Configuration.
     *
     * @throws Exception
     * @param Configuration $configuration
     */
    public function __construct(Configuration $configuration)
    {
        $configuration->verify();

        $this->configuration = $configuration;
        $this->connection    = new Connection($configuration);
    }

    /**
     * Override the default Connection
     *
     * @throws Exception
     * @param ConnectionInterface $connection
     * @return self
     */
    public function setConnection(ConnectionInterface $connection)
    {
        $this->connection = $connection;
        return $this;
    }

    /**
     * @return Configuration
     */
    public function getConfiguration()
    {
        return $this->configuration;
    }

    /**
     * Notify on an error message.
     *
     * @param string $message
     * @param array $backtrace
     * @param null $extraParams
     * @return string
     */
    public function notifyOnError($message, array $backtrace = null, $extraParams = null)
    {
        if (!$backtrace) {
            $backtrace = debug_backtrace();
            if (count($backtrace) > 1) {
                array_shift($backtrace);
            }
        }

        $notice = new Notice;
        $notice->load(array(
            'errorClass'   => 'PHP::Error',
            'backtrace'    => $backtrace,
            'errorMessage' => $message,
            'extraParameters'  => $extraParams,
        ));

        return $this->notify($notice);
    }

    /**
     * Notify on an exception
     *
     * @param Exception $e
     * @param null $extraParams
     * @return string
     */
    public function notifyOnException(Exception $e, $extraParams = null)
    {
        $notice = new Notice;
        $notice->load(array(
            'errorClass'       => get_class($e),
            'backtrace'        => $this->cleanBacktrace($e->getTrace() ?: debug_backtrace()),
            'errorMessage'     => $e->getMessage().' in '.$this->cleanFilePath($e->getFile()).' on line '.$e->getLine(),
            'extraParameters'  => $extraParams,
        ));

        return $this->notify($notice);
    }

    /**
     * Notify about the notice.
     *
     * @param Notice $notice
     * @return string|bool
     */
    public function notify(Notice $notice)
    {
        return $this->connection->send($notice);
    }

    /**
     * Clean the backtrace of unneeded junk.
     *
     * @param array $backtrace
     * @return array
     */
    protected function cleanBacktrace($backtrace)
    {
        foreach ($backtrace as &$item) {
            if (isset($item['file'])) {
                $item['file'] = $this->cleanFilePath($item['file']);
            }
            unset($item['args']);
        }

        return $backtrace;
    }

    protected function cleanFilePath($filePath)
    {
        $projectRoot = $this->configuration->get('projectRoot');
        return empty($projectRoot) ? $filePath : preg_replace("#^$projectRoot#", '[project_root]', $filePath);
    }
}


<?php
namespace Airbrake;

use SimpleXMLElement;

/**
 * Airbrake notice class.
 *
 * @package    Airbrake
 * @author     Drew Butler <drew@dbtlr.com>
 * @copyright  (c) 2011-2013 Drew Butler
 * @license    http://www.opensource.org/licenses/mit-license.php
 */
class Notice extends Record
{
    /** @var array */
    protected $dataStore = array(
        'backtrace' => null,
        'errorClass' => null,
        'errorMessage' => null,
        'extraParameters' => null,
    );

    /**
     * Convert the notice to xml
     *
     * @param Configuration $configuration
     * @return string
     */
    public function toXml(Configuration $configuration)
    {
        $doc = new SimpleXMLElement('<notice />');
        $doc->addAttribute('version', Version::API);
        $doc->addChild('api-key', $configuration->get('apiKey'));

        $notifier = $doc->addChild('notifier');
        $notifier->addChild('name', Version::NAME);
        $notifier->addChild('version', Version::NUMBER);
        $notifier->addChild('url', Version::APP_URL);

        $env = $doc->addChild('server-environment');
        $env->addChild('project-root', $configuration->get('projectRoot'));
        $env->addChild('environment-name', $configuration->get('environmentName'));
        $env->addChild('hostname', $configuration->get('hostname'));
        $env->addChild('app_version', $configuration->get('appVersion'));

        $error = $doc->addChild('error');
        $error->addChild('class', $this->get('errorClass'));
        $error->addChild('message', htmlspecialchars($this->get('errorMessage')));

        $trace = $this->get('backtrace');

        if (count($trace) > 0) {
            $backtrace = $error->addChild('backtrace');
            foreach ($trace as $entry) {
                $method = isset($entry['class']) ? $entry['class'].'::' : '';
                $method .= isset($entry['function']) ? $entry['function'] : '';
                $line = $backtrace->addChild('line');
                $line->addAttribute('file', isset($entry['file']) ? $entry['file'] : '');
                $line->addAttribute('number', isset($entry['line']) ? $entry['line'] : '');
                $line->addAttribute('method', $method);
            }
        }

        $request = $doc->addChild('request');
        $request->addChild('url', $configuration->get('url'));
        $request->addChild('component', $configuration->get('component'));
        $request->addChild('action', $configuration->get('action'));

        $extras = $this->get('extraParameters');
        $configExtras = $configuration->get('extraParameters');

        if (!isset($extras)) {
            $extras = array();
        }
        if (!isset($configExtras)) {
            $configExtras = array();
        }

        $innerExtras = array_merge($extras, $configExtras);

        $params = array_merge($configuration->getParameters(), array('airbrake_extra' => $innerExtras));

        $this->array2Node($request, 'params', $params);
        $this->array2Node($request, 'session', $configuration->get('sessionData'));
        $this->array2Node($request, 'cgi-data', $configuration->get('serverData'));

        return $doc->asXML();
    }

    /**
     * Add a Airbrake var block to an XML node.
     *
     * @param SimpleXMLElement $parentNode
     * @param string $key
     * @param array $params
     **/
    protected function array2Node($parentNode, $key, $params)
    {
        if (count($params) == 0) {
            return;
        }

        $node = $parentNode->addChild($key);
        foreach ($params as $key => $value) {
            if (is_array($value) || is_object($value)) {
                $value = json_encode((array) $value);
            }

            // htmlspecialchars() is needed to prevent html characters from breaking the node.
            $node->addChild('var', htmlspecialchars($value))
                ->addAttribute('key', $key);
        }
    }
}


<?php

namespace Airbrake;

/**
 * Airbrake standard filter class. Used to filter post/get data before it is
 * sent over to the server. Intended use case is to filter out sensitive
 * information such as passwords or credit card details.
 *
 * @package    Airbrake
 * @author     Leon Szkliniarz <leon@llamadigital.net>
 * @copyright  (c) 2014 Leon Szkliniarz
 * @license    http://www.opensource.org/licenses/mit-license.php
 */
class Filter implements Filter\FilterInterface
{
    /** @var array */
    protected $keyParts = array();

    /**
     * Create a Filter class based on a string key_name. Expects key_name to be
     * in the same format as a form name - so to filter out the value inside
     * $_POST['user']['password'] you would give it the value 'user[password]'
     *
     * @param string $keyName
     */
    public function __construct($keyName)
    {
        $this->splitKeyName($keyName);
    }

    /**
     * Applies the current filters to the passed in array, unsetting any
     * elements which match the filter. Note that this works via reference and
     * will mutate its argument instead of returning a copy.
     *
     * @param string &$array
     */
    public function filter(&$array)
    {
        $current = &$array;

        $keys = array_keys($this->keyParts);
        $lastElement = end($keys);

        /**
         * This code is ugly and complicated because PHP has no way of unsetting
         * arbitrary depths inside arrays.
         *
         * The intended functionality is if you create a filter like:
         * 'form[subform][id]' then $_POST['form']['subform']['id'] will be
         * unset. This format was chosen because it's also how you would
         * represent that structure inside a form element name in your markup.
         *
         * It all works by keeping a reference to the current depth of the array,
         * iterating over key_parts (see splitKeyName), checking if the iterated
         * value exists as a key of the current reference and then setting the
         * current reference to be that key. If it's the last element of the
         * iteration then this value needs to be filtered and is removed.
         */
        foreach ($this->keyParts as $index => $keyPart) {
            if (!isset($current[$keyPart])) {
                break;
            }

            if ($index == $lastElement) {
                unset($current[$keyPart]);
                break;
            }

            $current = &$current[$keyPart];
        }
    }

    /**
     * @param string $keyName
     */
    protected function splitKeyName($keyName)
    {
        /**
         * This breaks a form name formatted post name into it's constituent
         * parts. e.g. form[subform][id] becomes an array containing:
         * ['form', 'subform', 'id']
         */
        $r = '/\[([^]]+)\]/';
        $parts = preg_split($r, $keyName, -1, PREG_SPLIT_DELIM_CAPTURE);
        $parts = array_filter($parts, 'strlen');
        $this->keyParts = $parts;
    }
}


<?php
namespace Airbrake;

use Airbrake\Exception as AirbrakeException;

/**
 * Airbrake configuration class.
 *
 * Loads via the inherited Record class methods.
 *
 * @package    Airbrake
 * @author     Drew Butler <drew@dbtlr.com>
 * @copyright  (c) 2011-2013 Drew Butler
 * @license    http://www.opensource.org/licenses/mit-license.php
 */
class Configuration extends Record
{
    /** @var array  */
    protected $dataStore = array(
        'apiKey' => null,
        'timeout' => null,
        'environmentName' => 'production',
        'serverData' => null,
        'getData' => null,
        'postData' => null,
        'sessionData' => null,
        'component' => null,
        'action' => null,
        'projectRoot' => null,
        'url' => null,
        'hostname' => null,
        'appVersion' => null,
        'secure' => false,
        'host' => 'api.airbrake.io',
        'resource' => '/notifier_api/v2/notices',
        'apiEndPoint' => null,
        'errorReportingLevel' => null,
        'extraParameters' => null,
        'proxyHost' => null,
        'proxyPort' => 80,
        'proxyUser' => null,
        'proxyPass' => null
    );

    /** @var array */
    protected $parameterFilters = array();

    /**
     * Load the given data array to the record.
     *
     * @param string $apiKey
     * @param array|\stdClass $data
     */
    public function __construct($apiKey, $data = array())
    {
        $data['apiKey'] = $apiKey;
        parent::__construct($data);
    }

    /**
     * Initialize the data source.
     */
    protected function initialize()
    {
        if ($this->get('serverData') === null) {
            $this->set('serverData', (array) $_SERVER);
        }

        if ($this->get('getData') === null) {
            $this->set('getData', (array) $_GET);
        }

        if ($this->get('postData') === null) {
            $this->set('postData', (array) $_POST);
        }

        if ($this->get('sessionData') === null && isset($_SESSION)) {
            $this->set('sessionData', (array) $_SESSION);
        }

        $serverData = $this->get('serverData');

        if (!$this->get('projectRoot')) {
            $projectRoot = isset($serverData['_']) ? $serverData['_'] : $serverData['DOCUMENT_ROOT'];
            $this->set('projectRoot', $projectRoot);
        }

        if (!$this->get('url')) {
            if (isset($serverData['REDIRECT_URL'])) {
                $this->set('url', $serverData['REDIRECT_URL']);
            } elseif (isset($serverData['SCRIPT_NAME'])) {
                $this->set('url', $serverData['SCRIPT_NAME']);
            }
        }

        if (!$this->get('hostname')) {
            $this->set('hostname', isset($serverData['HTTP_HOST']) ? $serverData['HTTP_HOST'] : 'No Host');
        }

        $protocol = $this->get('secure') ? 'https' : 'http';
        $endPoint = $this->get('apiEndPoint') ?: $protocol . '://' . $this->get('host') . $this->get('resource');
        $this->set('apiEndPoint', $endPoint);
    }

    /**
     * Get the combined server parameters. Note that these parameters will be
     * filtered according to a black list of key names to ignore. If you wish to
     * get the unfiltered results you should use the getUnfilteredParameters
     * method instead.
     *
     * @return array
     */
    public function getParameters()
    {
        $parameters = $this->getUnfilteredParameters();
        foreach ($this->parameterFilters as $filter) {
            /** @var \Airbrake\Filter\FilterInterface $filter */
            $filter->filter($parameters);
        }
        return $parameters;
    }

    /**
     * Get the combined server parameters without applying the registered
     * filters
     *
     * @return array
     */
    public function getUnfilteredParameters()
    {
        return array_merge($this->get('postData'), $this->get('getData'));
    }

    /**
     * Adds an entry to a black list of GET/POST parameter key names which
     * should not be sent to the Airbrake server. This should be used to prevent
     * sensitive information, such as passwords or credit card details from
     * leaving your application server via error logging.
     *
     * Nested keys are treated like html form names - e.g. the key name
     * my_form[id] would stop the value inside $_POST['my_form']['id']
     * from being sent.
     *
     * @param string|Filter\FilterInterface $keyName
     * @return self
     */
    public function addFilter($keyName)
    {
        if (!$keyName instanceof Filter\FilterInterface) {
            $keyName = new Filter($keyName);
        }

        $this->parameterFilters[] = $keyName;
        return $this;
    }

    /**
     * Adds an array of entries to a black list of GET/POST parameter key names
     * which should not be sent to the Airbrake server. This should be used to
     * prevent sensitive information, such as passwords or credit card details
     * from leaving your application server via error logging.
     *
     * Nested keys are treated like html form names - e.g. the key name
     * my_form[id] would stop the value inside $_POST['my_form']['id']
     * from being sent.
     *
     * @param array $keyNames
     * @return Configuration
     */
    public function addFilters($keyNames)
    {
        array_map(array($this, 'addFilter'), $keyNames);

        return $this;
    }

    /**
     * Clears the GET/POST request key name black list.
     *
     * @return Configuration
     */
    public function clearFilters()
    {
        $this->parameterFilters = array();

        return $this;
    }

    /**
     * Verify that the configuration is complete.
     */
    public function verify()
    {
        if (!$this->get('apiKey')) {
            throw new AirbrakeException(
                'Cannot initialize the Airbrake client without an ApiKey being set to the configuration.'
            );
        }
    }
}