<?php
/**
 *  Dolumar engine, php/html MMORTS engine
 *  Copyright (C) 2009 Thijs Van der Schaeghe
 *  CatLab Interactive bvba, Gent, Belgium
 *  http://www.catlab.eu/
 *  http://www.dolumar.com/
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

class Neuron_GameServer_Mappers_ChatMapper
{
	/**
	* We need one instance per player.
	*/
	public static function getInstance ()
	{
		static $in;
		if (!isset ($in))
		{
			$in = new self ();
		}
		return $in;
	}

	protected function __construct ()
	{

	}

	private $lastReceivedMessageId = null;

	public function getChannelID ($channel)
	{
		$db = Neuron_DB_Database::getInstance ();

		$check = $db->query
		("
			SELECT
				*
			FROM
				n_chat_channels
			WHERE
				c_c_name = '{$db->escape ($channel)}'
		");

		if (count ($check) == 0)
		{
			$id = $db->query
			("
				INSERT INTO
					n_chat_channels
				SET
					c_c_name = '{$db->escape ($channel)}'
			");
		}
		else
		{
			$id = $check[0]['c_c_id'];
		}

		return $id;
	}

	/**
	*	Return the $since id that is $amount messages ago.
	*/
	private function getNewSince ($channelId, $limit, $before = null)
	{
		$db = Neuron_DB_Database::getInstance ();

		$where = "TRUE";
		if (isset ($before))
		{
			$where = "c_m_id < " . intval ($before);
		}

		$limit ++;

		$sql = "
			SELECT
				c_m_id
			FROM
				n_chat_messages
			WHERE
				c_c_id = '{$channelId}' AND
				{$where}
			ORDER BY
				c_m_id DESC
			LIMIT
				{$limit}
		";

		$messages = $db->query ($sql);

		$count = count ($messages);
		if ($count > 0)
		{
			// minus one, because we need to get the first
			// message as well. This might result in showing
			// 21 messages, but that's okay.
			return $messages[$count - 1]['c_m_id'] - 1;
		}
		return null;
	}

	/**
	* Return the latest messages since $since.
	* $since should be the last received message ID.
	*/
	public function getMessages ($channelId, $limit = 20, $since = null, $before = null)
	{
		$db = Neuron_DB_Database::getInstance ();

		$additionalWhere = "";

		if (!isset ($since))
		{
			$since = $this->getNewSince ($channelId, $limit, $before);

			// raise the limit to load everything at once
			$limit ++;
		}

		// If since is null & before is set, return empty array.
		if ($since === null && isset ($before))
		{
			return array ();
		}

		$additionalWhere = "AND c_m_id > '" . $since . "' ";

		if (isset ($before))
		{
			$additionalWhere .= "AND c_m_id < '" . $before . "' ";
		}

		$sql = "
			SELECT
				*,
				UNIX_TIMESTAMP(c_date) AS date
			FROM
				n_chat_messages
			WHERE
				c_c_id = '{$channelId}'
				{$additionalWhere}
			ORDER BY
				c_m_id ASC
			LIMIT
				{$limit}
		";

		//echo $sql;

		$message = $db->query ($sql);

		return $this->getDataFromReader ($message);
	}

	public function getLastReceivedMessageId ()
	{
		return $this->lastReceivedMessageId;
	}

	public function sendMessage ($channelId, $message, Neuron_GameServer_Player $player)
	{
		$profiler = Neuron_Profiler_Profiler::getInstance ();

		$db = Neuron_DB_Database::getInstance ();

		$profiler->message ('Sending message');

		return $db->query
		("
			INSERT INTO
				n_chat_messages
			SET
				c_c_id = '{$db->escape ($channelId)}',
				c_plid = '{$player->getId ()}',
				c_date = NOW(),
				c_message = '{$db->escape ($message)}'
		");
	}

	private function getDataFromReader ($data)
	{
		$out = array ();
		foreach ($data as $v)
		{
			$model = $this->getRecordFromData ($v);
			$out[] = $model;

			// Do something special here, update to latest id
			$this->lastReceivedMessageId =
				max ($this->lastReceivedMessageId, $model->getId ());
		}
		return $out;
	}

	private function getRecordFromData ($data)
	{
		$message = new Neuron_GameServer_Models_ChatMessage ();

		$message->setId ($data['c_m_id']);
		$message->setPlid ($data['c_plid']);
		$message->setTimestamp ($data['date']);
		$message->setMessage ($data['c_message']);

		if (isset ($data['pu_read']))
		{
			$message->setRead ($data['pu_read']);
		}

		return $message;
	}

	/**
	* This method will be called whenever a user opens
	* a new window. This is basically to decide if
	* the mapper should "unblock" when receiving a
	* message or not.
	* Information will probalby be stored in session.
	* If the user does not request new messages for
	* a "timeout" period of time, we can "unregister"
	*/
	public function joinChannel (Neuron_GameServer_Player $player, $channelId)
	{
		// Does nothing when not blocking.
	}

	/**
	* For now, probably not used.
	*/
	public function leaveChannel (Neuron_GameServer_Player $player, $channelId)
	{
		// Does nothing when not blocking.
	}

	/**
	*CREATE TABLE  `n_privatechat_updates` (
	*`pu_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
	*`pu_from` INT NOT NULL ,
	*`pu_to` INT NOT NULL ,
	*`pu_date` DATETIME NOT NULL
	*) ENGINE = INNODB
	*/
	public function addPrivateChatUpdate ($msgid, Neuron_GameServer_Player $from, Neuron_GameServer_Player $target)
	{
		$msgid = intval ($msgid);

		$db = Neuron_DB_Database::getInstance ();

		$db->query
		("
			DELETE FROM
				n_privatechat_updates
			WHERE
				pu_to = {$target->getId ()} AND
				pu_from = {$from->getId ()}
		");

		return $db->query
		("
			INSERT INTO
				n_privatechat_updates
			SET
				pu_from = {$from->getId ()},
				pu_to = {$target->getId ()},
				c_m_id = $msgid,
				pu_date = NOW()
		");
	}

	/**
	* Set all messages to a certain player to "read"
	*/
	public function setPrivateChatUpdateRead (Neuron_GameServer_Player $from, Neuron_GameServer_Player $target)
	{
		$db = Neuron_DB_Database::getInstance ();

		$db->query
		("
			UPDATE
				n_privatechat_updates
			SET
				pu_read = '1'
			WHERE
				pu_to = {$target->getId ()} AND
				pu_from = {$from->getId ()}
		");
	}

	public function getPrivateChats (Neuron_GameServer_Player $target, $start = 0, $end = 10)
	{
		$db = Neuron_DB_Database::getInstance ();

		$start = intval ($start);
		$end = intval ($end);

		$data = $db->query
		("
			SELECT
				*,
				UNIX_TIMESTAMP(c_date) AS date
			FROM
				n_privatechat_updates
			LEFT JOIN
				n_chat_messages USING(c_m_id)
			WHERE
				n_privatechat_updates.pu_to = {$target->getId ()}
			ORDER BY
				n_privatechat_updates.pu_date DESC
			LIMIT
				$start, $end
		");

		return $this->getDataFromReader ($data);
	}

	/**
	* If $since is said, return all events since "since".
	* If $since is not said, return the single latest event.
	*
	* ($since is obviously the pu_id)
	*/
	public function getPrivateChatUpdates (Neuron_GameServer_Player $target, $since = null)
	{
		$db = Neuron_DB_Database::getInstance ();

		if (isset ($since))
		{
			$data = $db->query
			("
				SELECT
					*
				FROM
					n_privatechat_updates
				WHERE
					pu_id > ".intval($since)." AND
					pu_to = {$target->getId ()}
			");
		}

		else
		{
			$data = $db->query
			("
				SELECT
					*
				FROM
					n_privatechat_updates
				WHERE
					pu_to = {$target->getId ()}
				ORDER BY
					pu_id DESC
				LIMIT
					1
			");
		}

		return $data;
	}

	public function countUnreadMessages (Neuron_GameServer_Player $target)
	{
		$db = Neuron_DB_Database::getInstance ();

		$data = $db->query
		("
			SELECT
				COUNT(pu_id) AS aantal
			FROM
				n_privatechat_updates
			WHERE
				pu_to = {$target->getId ()}
				AND pu_read = '0'
		");

		return $data[0]['aantal'];
	}

	public function countPrivateMessages (Neuron_GameServer_Player $target)
	{
		$db = Neuron_DB_Database::getInstance ();

		$data = $db->query
		("
			SELECT
				COUNT(pu_id) AS aantal
			FROM
				n_privatechat_updates
			WHERE
				pu_to = {$target->getId ()}
		");

		return $data[0]['aantal'];
	}
}


<?php

namespace PhalApi\Foundation;

class Translator
{
    /**
     * @var array $message 翻译的映射
     */
    protected static $message = [];

    /**
     * @var string $language 语言
     */
    protected static $language = 'en';

    /**
     * 获取翻译
     *
     * @param string $key 翻译的内容
     * @param array $params 动态参数
     * @return mixed
     */
    public static function get($key, $params = [])
    {
        if (empty(self::$message)) {
            self::setLanguage('en');
        }

        $rs = isset(self::$message[$key]) ? self::$message[$key] : $key;

        $names = array_Keys($params);
        $names = array_map('\PhalApi\Foundation\Translator::formatVar', $names);

        return str_replace($names, array_values($params), $rs);
    }

    /**
     * 语言设置
     *
     * @param string $language 翻译包的目录名
     * @return void
     */
    public static function setLanguage($language)
    {
        self::$language = $language;

        self::$message = [];

        self::addMessage(dirname(__FILE__) . '/..');

        if (defined('API_ROOT')) {
            self::addMessage(API_ROOT);
        }
    }

    /**
     * 添加更多翻译
     *
     * @param string $path 待追加的路径
     * @return void
     */
    public static function addMessage($path)
    {
        $moreMessagePath = self::getMessageFilePath($path, self::$language);

        if (file_exists($moreMessagePath)) {
            self::$message = array_merge(self::$message, include $moreMessagePath);
        }
    }

    /**
     * 获取当前的语言
     *
     * @return string
     */
    public static function getLanguage()
    {
        return self::$language;
    }

    public static function formatVar($name)
    {
        return '{' . $name . '}';
    }

    protected static function getMessageFilePath($root, $language)
    {
        return implode(DIRECTORY_SEPARATOR, [$root, 'language', strtolower($language), 'common.php']);
    }
}


<?php
/**
 * Just Framework - It's a PHP micro-framework for Full Stack Web Developer
 *
 * @package     Just Framework
 * @copyright   2016 (c) Mahmoud Elnezamy
 * @author      Mahmoud Elnezamy <http://nezamy.com>
 * @link        http://justframework.com
 * @license     http://www.opensource.org/licenses/mit-license.php MIT
 * @version     1.0.1
 */
namespace System\Support;
/**
 * Arr
 *
 * @package     Just Framework
 * @author      Mahmoud Elnezamy <http://nezamy.com>
 * @since       1.0.1
 */
class Arr
{
	/**
	* Get value from nested array.
	*
	* @param array 	$arr
	* @param string    $k
	* @param string 	$default
	*
	* @return mixed
	*/
	public static function get(array $arr, $k, $default=null)
	{
		if ( isset($arr[$k]) ) return $arr[$k];

		$nested = explode('.',$k);
		foreach ( $nested as $part ) {
		    if (isset($arr[$part])) {
				$arr = $arr[$part];
				continue;
		    } else {
				$arr = $default;
				break;
		    }
		}
		return $arr;
	}

	/**
     * set value to nested array.
     *
     * @param array 	$arr
     * @param string    $k
     * @param mixed 	$v
     *
     * @return array
     */
	public static function set(array $arr, $k, $v)
	{
		$nested = !is_array($k) ? explode('.',$k) : $k;
		$count = count($nested);
		if ($count == 1){
			return $arr[$k] = $v;
		}
		elseif ($count > 1)
		{
			$prev = '';
			$loop = 1;
			$unshift = $nested;

			foreach ($nested as $part)
			{
				if (isset($arr[$part]) && $count > $loop)
				{
					$prev = $part;
					array_shift($unshift);
					$loop++;
					continue;
				}
				else
				{
					if ( $loop > 1 && $loop < $count )
					{
						if( !isset($arr[$prev][$part]) ) $arr[$prev][$part] = [];

						$arr[$prev] = static::set($arr[$prev], $unshift, $v);
						$loop++; break;
					}
					elseif ( $loop >= 1 && $loop == $count )
					{
						if ( !is_array($arr[$prev]) ) $arr[$prev] = [];

						if ($part == '')
							$arr[$prev][] = $v;
						else
							$arr[$prev][$part] = $v;
					}else{
						$arr[$part] = [];
						$prev = $part;
						array_shift($unshift);
						$loop++;
					}
				}
	        }
		}
		return $arr;
	}

	/**
     * Get value if key exists or default value.
     *
     * @param array 	$arr
     * @param string    $k
     * @param string 	$default
     *
     * @return mixed
     */
	public static function value(array $arr, $k, $default=null){
		return isset($arr[$k]) ? $arr[$k] : $default;
	}

	/**
     * Get value from string json.
     *
     * @param string 	$jsonStr
     * @param string    $k
     * @param string 	$default
     *
     * @return mixed
     */
	public static function json($jsonStr, $k=null, $default=null){
		$json = json_decode($jsonStr, true);
		if($k && $json){
			return self::get($json, $k, $default);
		}
		return $json;
	}

	public static function replace_values($arr, $from = null, $to = "")
	{
		$results = [];
		foreach ($arr as $k => $v) {
			$toArr = (array) $v;
			if(count($toArr) > 1 || is_array($v)){
				$results[$k] = nullToString($toArr);
			} else {
				$results[$k] = $v;
				if ($v == $from) {
					$results[$k] = $to;
				}
			}
		}
		return $results;
	}
}


<?php declare(strict_types=1);

namespace Roquie\Database;

use Monolog\Formatter\LineFormatter;
use Monolog\Handler\ErrorLogHandler;
use Monolog\Logger;

class PrettyLogger
{
    public const CHANNEL = 'Rdb';

    /**
     * Create Monolog logger without fucking brackets -> [] []  [] []  [] []  [] []  [] []
     * if context and extra is empty.
     *
     * @param string $channel
     * @return \Monolog\Logger
     */
    public static function create(string $channel = self::CHANNEL): Logger
    {
        $logger = new Logger($channel);
        $handler = new ErrorLogHandler();
        $formatter = new LineFormatter('[%datetime%] %channel%.%level_name%: %message% %context% %extra%');
        $formatter->ignoreEmptyContextAndExtra();

        $handler->setFormatter($formatter);
        $logger->pushHandler($handler);

        return $logger;
    }
}


<?PHP
namespace Cora;

class Route extends Framework
{

    protected $container;           // VARIES   - For passing data to controllers.
    protected $pathString;          // STRING   - URL excluding siteURL. mysite.com/FROM/HERE/ONWARD/
    protected $path;                // ARRAY    - Array version of pathString. URL pieces after baseURL.
    protected $controllerPath;      // STRING   - filepath to controller .php file.
    protected $controllerOffset;    // INT      - The offset within the path array of the controller.
    protected $controllerName;      // STRING
    protected $controllerNamespace; // STRING
    protected $controller;          // OBJECT
    protected $method;              // STRING
    protected $methodArgs;          // ARRAY    - If set, this will override any arguments passed in through URL.
    protected $httpMethod;          // STRING
    protected $collectionIDgiven;   // BOOL     - If the URL is of form /articles/107 this is set to true.
    protected $collectionID;        // INT      - The ID if a collection ID is specified.

    protected $paths;               // ARRAY    - Holds custom defined paths.
    protected $pathRESTful = true;  // BOOL     - For enabling/disabling RESTful on individual custom routes.

    /**
     *  Sets up fixed resources.
     */
    public function __construct($container = false)
    {
        parent::__construct(); // Call parent constructor too so we don't lose functionality.

        // For site specific data. This will be passed to Cora's controllers when they
        // are invoked in the routeExec() method.
        $this->container = $container;

        // Assign custom paths
        $paths = [];
        if (file_exists($this->config['basedir'].'cora/app/Paths.php')) {
            include($this->config['basedir'].'cora/app/Paths.php');
        }
        $this->paths = $paths;

        // Namespacing Defaults
        $this->controllerNamespace = 'Controllers\\';

    }

    /**
     *  Processes a route (which in turn sets the PATH, checks custom and default routes),
     *  then executes the route or lack thereof.
     */
    public function run($uri = false, $method = false)
    {
        // Setup and check for route.
        $this->routeProcess($uri, $method);

        // Debug
        $this->debug('Route: ' . $this->pathString);

        // Execute route (will load 404 if not exists)
        $this->routeExec();
    }

    /**
     *  Checks if a custom or default route exists for a URL and HTTP method.
     *  By default will grab URL and HTTP method from server variables,
     *  but can be passed in as arguments.
     */
    public function routeProcess($uri = false, $httpMethod = false)
    {
        // Set Request type.
        if (!$httpMethod) $httpMethod = $_SERVER['REQUEST_METHOD'];
        $this->httpMethod = $httpMethod;

        // Set PATH info.
        if (!$uri) $uri = $_SERVER['REQUEST_URI'];
        $this->setPath($uri);

        // BIG IDEA: routeFind() must be called to populate needed Controller and Method variables.
        //
        // If a custom route is found, routeFind() still needs to be called to populate
        // data member variables and complete the mapping from URI to a Controller file.
        //
        // If custom route is not set, but automatic routing is enabled, also call routeFind
        // to see if an automatic route exists.
        $customPathSearch = $this->customFind();
        if ($customPathSearch || (!$customPathSearch && $this->config['automatic_routing'])) {
            $this->routeFind();
        }

        // If a route was found, but access was denied via the preExec callback...
        else if ($customPathSearch == -1) {
            $this->error('403');
        }

        // ELSE
        // Do nothing if there's no match. When and if routeExec is called user will be
        // directed to 404. But until then, developers can use the exists() method to
        // check different routes and possibly redirect the user somewhere else.
    }

    /**
     *  Checks if a given URI and HTTP Method can be mapped to Controller.
     *
     *  If no URI and HTTP Method are passed in, then it will make the assumption that
     *  routeProcess has already been called and simply see if a controller was found.
     *  As such, either make sure you either explicitely state what resource you want to check
     *  or manually call routeProcess() before calling this method.
     *
     *  Note: This helps with integrating Cora into legacy applications.
     *  You can check if a matching controller was found in the directory you're putting
     *  Cora controllers in, and if not, then run legacy routing instead.
     */
    public function exists($uri = false, $httpMethod = false)
    {
        // If wanting to check if a passed in uri and method have a path mapping
        if ($uri && $httpMethod) {
            $this->routeProcess($uri, $httpMethod);
        }

        // After a URI is processed, it should result in a controller path being set if a
        // matching one exists. If not set, that means there was no matching controller to given URI.
        if (!isset($this->controllerPath)) {
            return false;
        }
        return true;
    }

    /**
     *  Uses the info generated by routeFind() to then create an instance of the
     *  appropriate controller and call the desired method.
     */
    public function routeExec()
    {

        // If no controller was found by routeFind()...
        if (!isset($this->controllerPath)) {
            $this->debug('routeExec: No controllerPath set. Routing to 404.');
            $this->error('404');
            exit;
        }

        // Grab method arguments from the URL.
        if (isset($this->methodArgs)) {
            $methodArgs = $this->methodArgs;
        }
        else {
            if ($this->collectionIDGiven) {
                $methodArgs = $this->partialPathArray($this->controllerOffset+1, 1);
            }
            else {
                $methodArgs = $this->partialPathArray($this->controllerOffset+2);
            }

            // Remove the last item from arguments if empty.
            $lastItem = count($methodArgs)-1;
            if (isset($methodArgs[$lastItem]) && $methodArgs[$lastItem] == '') {
                array_pop($methodArgs);
            }

            // If no arguments are set then make empty array.
            if (empty($methodArgs) || $methodArgs[0] == '') {
                $methodArgs = array();
            }
            else {
                // Sanitize arguments.
                $input = new Input($methodArgs);
                $methodArgs = $input->getData();
            }
        }

        /** Maps an array of arguments derived from the URL into a method with a comma
         *  delimited list of parameters. Calls the method.
         *
         *  I.E. If the URL is:
         *  'www.mySite.com/MyController/FooBar/Param1/Param2/Param3'
         *
         *  And the FooBar method within MyController is defined as:
         *  public function FooBar($a, $b, $c) {}
         *
         *  $a will have the value 'Param1'
         *  $b will have the value 'Param2'  ... and so forth.
         */
        call_user_func_array(array($this->controller, $this->method), $methodArgs);

    } // end routeExec

    /**
     *  Checks if a custom path exists for the current URL.
     *
     *  This only checks if the URL stored in the Route class matches any custom defined
     *  paths for the application. It does NOT guarantee that there's any Controller that
     *  matches that path. In order to finish the mapping from URL to physical Controller, routeFind()
     *  will need to be called after this method.
     *
     *  @preConditions paths, pathString, and httpMethod data member variables must be set.
     *  @return 1 if matching custom route
     *          0 if no matching custom route
     *         -1 if matching route, but access denied by preExec callback.
     */
    protected function customFind()
    {
        // Setup
        $url = '//'.$this->pathString;
        $matchFound = false;
        $templateRegex = "/\{\w+}/";
        $pathsExecuted = [];

        $numPaths = count($this->paths);
        for($i=0; $i<$numPaths; $i++) {
            $path = $this->paths->get($i);

            // Only process this custom route if it hasn't been executed before.
            if (!in_array($path->url, $pathsExecuted)) {
                ///////////////////////////////////////////////
                // Grab path URL template variables
                ///////////////////////////////////////////////
                /**
                *  Create an array of path variables from custom path URL definition.
                *  I.E. users/{someVariable}/{anotherVariable}
                */
                $templateVariables = [];
                preg_match_all($templateRegex, $path->url, $templateVariables);

                ///////////////////////////////////////////////
                // Replacing templates variables with regex
                ///////////////////////////////////////////////
                /**
                *  This is for replacing the bracket variables in the custom route with a regular expression string.
                *  If no custom definition for the variable was set in the variable definitions section, then the variable
                *  will default to alpha-numeric with underscore and dash.
                *  INPUT   = users/action-subaction/23
                *  OUTPUT  = users\/([a-zA-Z0-9-_]+)\-([a-zA-Z0-9-_]+)\/([0-9]+)
                */
                $urlRegex = preg_quote($path->url, '/');

                foreach ($templateVariables[0] as $key => $placeholder) {
                    if (isset($path->def[$placeholder])) {
                        $urlRegex = str_replace(preg_quote($placeholder), '('.$path->def[$placeholder].')', $urlRegex);
                    }
                    else if ($placeholder == '{anything}') {
                        $urlRegex = str_replace(preg_quote($placeholder), '(.+)', $urlRegex);
                    }
                    else {
                        $urlRegex = str_replace(preg_quote($placeholder), '([a-zA-Z0-9_]+)', $urlRegex);
                    }
                }

                ///////////////////////////////////////////////
                // Check for regex match against URL given
                ///////////////////////////////////////////////
                /**
                *   This takes the current URL and checks if it matches the custom route we are currently looking at.
                *   If there's a match, then it marks that we found a valid custom path.
                *   $urlData will get populated with the matching variable values from the URL. This works because
                *   all the regexes from the above section are within parenthesis.
                *   With the following regex: "users/([a-zA-Z0-9_]+)/([a-zA-Z0-9_]+)"
                *   The match for the first set of parenthesis will get placed in $urlData[1], the 2nd set in $urlData[2], etc.
                *
                *   See below for an example:
                *   if URL              = articles/grab-all/23
                *   and custom route    = articles/{action}-{modifier}/{id}
                *   $urlData[1] will be 'grab', $urlData[2] will be 'all', and $urlData[3] will be 23.
                */
                $urlVars = []; // Associative array
                $urlData = [];
                $finalRoute = $path->route;
                if (preg_match("/$urlRegex/", $url, $urlData)) {
                    $matchFound = true;
                    $this->debug('Matching Custom Route! Real URL "' . $url . '" matches route "' . $path->url . '" which routes to "' . $path->route . '"');
                    // A match was found, so at this point $urlData contains the matching pieces from the regex.
                    // With the first variable match being in $urlData[1]. Offset 0 is useless, so let's unset it so it
                    // does screw up our count.
                    unset($urlData[0]);
                    for($i=1; $i<=count($urlData); $i++) {
                        // For each REAL variable value from our URL, let's replace any references to that variable in our
                        // route with the actual values.
                        // Example:
                        //                     Path URL = "users/{action}-{modifier}"
                        //    Actual URL in web browser = "users/fetch-all"
                        //                   Path Route = "users/{action}/{modifier}"
                        //  Final Route after this loop = "users/fetch/all"
                        $pathVar = $templateVariables[0][$i-1]; // E.g. {action}. $templateVariables starts at offset 0.
                        $pathVarValue = $urlData[$i];
                        $finalRoute = str_replace($pathVar, $pathVarValue, $finalRoute);

                        // Add variable to associative array for variables from URL
                        $urlVariableName = substr($templateVariables[0][$i-1], 1, -1);
                        $urlVars[$urlVariableName] = $urlData[$i];
                    }
                } else {
                    if ($path->debug) {
                        $this->debug('FAILED Matching Route! Real URL "' . $url . '" does not match route "' . $path->url . '" which routes to "' . $path->route . '"');
                    }
                }

                // Run preMatch function for this path.
                if ($matchFound && !$path->preMatchCheck($urlVars, $this->container)) {
                    $matchFound = false;
                }

                ///////////////////////////////////////////////
                // Handle Path match
                ///////////////////////////////////////////////
                /**
                *  If this iteration of the loop found a match to a custom path,
                *  Then execute that Path's pre execution function and if that returns true,
                *  then set our new Route to be the one defined in the custom Path.
                */
                if ($matchFound) {
                    // If the path accepts all http method types or the current request type is listed as being accepted.
                    if (
                        stripos($path->actions, 'all') !== false ||
                        stripos($path->actions, $this->httpMethod) !== false
                    ) {
                        // Run preExec function for this path.
                        if (!$path->preExecCheck($urlVars, $this->container)) {
                            return -1;
                        }

                        // If an internal route was defined for this path, set that route.
                        if ($path->route) {
                            $this->setPath($finalRoute);
                        }

                        // If an internal route was defined for this path, set that route.
                        if (isset($path->args)) {
                            $this->methodArgs = $path->getArgs($urlVars, $this->container);
                        }

                        // If path is passive, set the path route to be the URL (if necessary) and if so, reset custom paths
                        // iteration.
                        if ($path->passive == true) {
                            if ($path->route) {
                                $url = $finalRoute;
                                $i = 0;

                                // Add path to list of executed paths.
                                $pathsExecuted[] = $path->url;
                            }
                        }

                        // If the path is isn't passive, try to find matching route.
                        else {
                            $this->pathRESTful = $path->RESTful;
                            return 1;
                        }
                    }
                }
            } // end if not in executed paths list
        } // end for loop
        return 0;
    }

    /**
     *  Given a URL, sets needed PATH information.
     *  This must be executed before routeFind as it sets the needed pathString var.
     *
     *  NOTE: $_SERVER['QUERY_STRING'] is set by the server and may not be available
     *  depending on the server being used.
     */
    protected function setPath($url)
    {
        // Removes any GET data from the url.
        // Makes 'mysite.com/websites/build/?somevalue=test' INTO '/websites/build'
        $cleanURI = str_replace('?'.$_SERVER['QUERY_STRING'], '', $url);

        // Removes the 'mysite.com' from 'mysite.com/controller/method/id/'
        // Resulting pathString will be 'controller/method/id/'
        $this->pathString = explode($this->config['site_url'], $cleanURI, 2)[1];

        // If config option to make url lowercase is true
        if ($this->config['lowercase_url']) {
            $this->pathString = strtolower($this->pathString);
        }

        // Setup Path array
        $this->path = explode('/', $this->pathString);
    }

    /**
     *  Searches through $path to figure out what part of it is the controller.
     *  This requires searching through the filesystem.
     *
     *  If   $path = /folder1/folder2/Controller/Method/Id
     *  Then $controllerPath    = '/folder1/folder2/Controller'
     *  And  $controllerOffset  = 2
     *
     *  NOTE: This is a recursive function.
     */
    protected function routeFind($basePath = '', $offset = 0)
    {
        $this->debug('');

        // Vars setup
        $curPath = $this->partialPathArray($offset, 1);
        $controller = '';
        $controllerFileName = '';

        $this->debug('Current Path: '.implode('/', $curPath));

        // if $curPath isn't empty
        if (is_array($curPath) and !empty($curPath) and $curPath[0] != '') {
            $controller_path = $curPath[0];
        }
        // Else grab the default controller.
        else {
            $controller_path = $this->config['default_controller'];
        }
            $controller = $this->getClassName($controller_path);
            $controllerFileName =   $this->config['controllersPrefix'] .
                                    $controller .
                                    $this->config['controllersPostfix'] .
                                    '.php';

        // Set working filepath.
        $dirpath = $this->config['pathToControllers'].$basePath.$controller_path;
        $filepath = $this->config['pathToControllers'].$basePath.$controllerFileName;

        // Debug
        $this->debug('Searching for: ' . $filepath);

        // Check if the controller .php file exists in this directory.
        if (file_exists($filepath)) {
            $this->controllerPath = $this->partialPathString(0, $offset+1);
            $this->controllerOffset = $offset;
            $this->controllerName = $controller;

            $this->debug('File Found: ' . $controllerFileName);
            $this->debug('Controller Path: ' . $this->controllerPath);

            // Create instance of controller and check if that method exists in it. Otherwise we want to keep
            // looking for other sub-folder classes that might map to the url.
            $controllerInstance = $this->getController();
            $method = $this->getMethod();
            $this->debug("getMethod() call in routeFind() returns: ".$method);

            if (!is_callable(array($controllerInstance, $method))) {
                // Update namespace
                $this->controllerNamespace .= $this->controllerName.'\\';

                //  Controller->Method combo doesn't exist, so undo previously set data.
                $this->controllerPath = null;
                $this->controllerOffset = null;
                $this->controllerName = null;

                // Debug
                $this->debug('Not matching method within controller. Continuing search.');

            } else {
                // Valid controller+method combination found, so stop searching further.
                $this->controller   = $controllerInstance;
                $this->method       = $method;
                return true;
            }
        }
        else {
            // Update namespace
            $this->controllerNamespace .= $controller.'\\';
        }

        // Else check if there's a matching directory we can look through.
        if (is_dir($dirpath)) {
            $this->debug('Directory Found: ' . $basePath . $controller);

            // Recursive call
            $this->routeFind($basePath . $controller_path . '/', $offset+1);
        }

    } // end routeFind

    protected function getController()
    {

        // Load generic Cora parent class
        require_once('Cora.php');

        // Include the controller code.
        $cPath =    $this->config['pathToControllers'] .
                    $this->getPath($this->controllerPath) .
                    $this->config['controllersPrefix'] .
                    $this->controllerName .
                    $this->config['controllersPostfix'] .
                    '.php';

        if (file_exists($cPath)) {
            include_once($cPath);
        }
        // Return an instance of the controller.
        $class = $this->controllerNamespace.$this->getClassName($this->controllerName);

        return new $class($this->container);
    }

    protected function getMethod()
    {

        $method = '';
        $this->collectionIDGiven = false;
        $this->collectionID = false;

        // Figure out method to be called, or use default.
        if (isset($this->path[$this->controllerOffset+1])) {
            $method = $this->path[$this->controllerOffset+1];

            // If no method is specified or if the method spot is numeric (indicating
            // that an ID is being passed to a collection for RESTful routing)
            // Route to default method.
            if ($method == '') {
                $method = $this->config['default_method'];
            }
            else if (is_numeric($method)) {
                $this->collectionIDGiven = true;
                $this->collectionID = $method;
                $method = $this->config['default_method'];
            }
        }
        else {
            $method = $this->config['default_method'];
        }

        // RESTful routing:
        // Modify method routed to if request is not of type GET.
        if ($this->config['enable_RESTful'] && $this->pathRESTful) {
            if ($this->collectionIDGiven) {
                switch ($this->httpMethod) {
                    case 'GET':
                        $method = 'itemGET';
                        break;
                    case 'PUT':
                        $method = 'itemPUT';
                        break;
                    case 'POST':
                        $method = 'itemPOST';
                        break;
                    case 'DELETE':
                        $method = 'itemDELETE';
                        break;
                }
            }
            else {
                switch ($this->httpMethod) {
                    case 'PUT':
                        $method = $method.'PUT';
                        break;
                    case 'POST':
                        $method = $method.'POST';
                        break;
                    case 'DELETE':
                        $method = $method.'DELETE';
                        break;
                }
            }
        }

        return $method;

    } // END getMethod

    protected function getClassName($slug)
    {
        return str_replace(' ', '', ucwords(preg_replace('/[-_]/', ' ', $slug)));
    }

    /**
     *  The following two methods work off the URL path stored in $this->path.
     *  They are used to return part of that path in either Array or String form
     *  when asked to by the recursive calls of routeFind().
     */
    protected function partialPathString($offset, $length = null, $dataArray = false)
    {
        if ($dataArray == false) {
            $dataArray = $this->path;
        }
        $partialPathArray = array_slice($dataArray, $offset, $length);
        return implode('/', $partialPathArray);
    }

    protected function partialPathArray($offset, $length = null)
    {
        return array_slice($this->path, $offset, $length);
    }

    /**
     *  Sample Types:
     *  401 = Access Denied
     *  404 = Not Found
     */
    protected function error($type)
    {
        $filepath = $this->config['basedir'].'cora/app/Error.php';

        if (file_exists($filepath)) {
            $error = new \Cora\App\Error($this->container);
        }
        else {
            $error = new \Cora\Error($this->container);
        }
        $error->handle($type);
    }

} // end Class


<?php
/**
 * Phossa Project
 *
 * PHP version 5.4
 *
 * @category  Library
 * @package   Phossa2\Shared
 * @copyright Copyright (c) 2016 phossa.com
 * @license   http://mit-license.org/ MIT License
 * @link      http://www.phossa.com/
 */
/*# declare(strict_types=1); */

namespace Phossa2\Shared\Tree;

use Phossa2\Shared\Base\ObjectAbstract;

/**
 * Dealing with tree structure
 *
 * @package Phossa2\Shared
 * @author  Hong Zhang <phossa@126.com>
 * @see     ObjectAbstract
 * @see     TreeInterface
 * @version 2.0.6
 * @since   2.0.3 added
 * @since   2.0.5 added deleteNode(), using TreeInterface
 */
class Tree extends ObjectAbstract implements TreeInterface
{
    /**
     * node splitter
     *
     * @var    string
     * @access protected
     */
    protected $splitter = '.';

    /**
     * the result tree
     *
     * @var    array
     * @access protected
     */
    protected $tree;

    /**
     * construct a tree
     *
     * @param  array $data
     * @param  string $splitter
     * @access public
     * @api
     */
    public function __construct(array $data = [], /*# string */ $splitter = '.')
    {
        $this->splitter = $splitter;
        $this->tree = empty($data) ? $data : $this->fixTree($data);
    }

    /**
     * {@inheritDoc}
     */
    public function getTree()/*# : array */
    {
        return $this->tree;
    }

    /**
     * {@inheritDoc}
     */
    public function &getNode(/*# string */ $nodeName)
    {
        if ('' === $nodeName) {
            $result = &$this->tree;
        } else {
            $result = &$this->searchNode($nodeName, $this->tree, false);
        }
        return $result;
    }

    /**
     * {@inheritDoc}
     */
    public function hasNode(/*# string */ $nodeName)/*# : bool */
    {
        if (null === $this->getNode($nodeName)) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * {@inheritDoc}
     */
    public function addNode(/*# string */ $nodeName, $data)
    {
        // get the node
        $node = &$this->searchNode($nodeName, $this->tree);

        // fix data
        if (is_array($data)) {
            $data = $this->fixTree($data);
        }

        // merge
        if (is_array($node) && is_array($data)) {
            $node = array_replace_recursive($node, $data);
        } else {
            $node = $data;
        }

        return $this;
    }

    /**
     * {@inheritDoc}
     */
    public function deleteNode(/*# string */ $nodeName)
    {
        if ('' === $nodeName) {
            $this->tree = [];
        } else {
            $current = &$this->getNode($nodeName);
            if (null !== $current) {
                $split = explode($this->splitter, $nodeName);
                $name  = array_pop($split);
                $upper = &$this->getNode(join($this->splitter, $split));
                unset($upper[$name]);
            }
        }
        return $this;
    }

    /**
     * {@inheritDoc}
     */
    public function getDelimiter()/*# : string */
    {
        return $this->splitter;
    }

    /**
     * Fix array, convert flat name to tree node name
     *
     * @param  array $data
     * @return array
     * @access protected
     */
    protected function fixTree(array $data)/*# : array */
    {
        $result = [];
        foreach ($data as $k => $v) {
            $res = &$this->searchNode($k, $result);
            if (is_array($v) && is_array($res)) {
                $res = array_replace_recursive($res, $this->fixTree($v));
            } else {
                $res = $v;
            }
        }
        return $result;
    }

    /**
     * Search a node in the $data
     *
     * @param  string $path
     * @param  array &$data
     * @param  bool $create
     * @return mixed null for not found
     * @access protected
     * @since  2.0.6 bug fix
     */
    protected function &searchNode(
        /*# string */ $path,
        array &$data,
        /*# bool */ $create = true
    ) {
        $found = &$data;
        foreach (explode($this->splitter, $path) as $k) {
            $found = &$this->childNode($k, $found, $create);
            if (null === $found) {
                break;
            }
        }
        return $found;
    }

    /**
     * get or create the next/child node, return NULL if not found
     *
     * @param  string $key
     * @param  mixed $data
     * @param  bool $create create the node if not exist
     * @return mixed
     * @access protected
     */
    protected function &childNode(
        /*# string */ $key,
        &$data,
        /*# bool */ $create
    ) {
        $null = null;
        if (is_array($data)) {
            if (isset($data[$key])) {
                return $data[$key];
            } elseif ($create) {
                $data[$key] = [];
                return $data[$key];
            }
        }
        return $null;
    }
}


<?php

/**
 * ExtDirect\Collections\ResponseCollection
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to version 3 of the GPL license,
 * that is bundled with this package in the file LICENSE, and is
 * available online at http://www.gnu.org/licenses/gpl.txt
 *
 * PHP version 5
 *
 * @category  ExtDirect
 * @package   ExtDirect
 * @author    Philipp Dittert <philipp.dittert@gmail.com>
 * @copyright 2015 Philipp Dittert
 * @license   http://www.gnu.org/licenses/gpl.txt GNU General Public License, version 3 (GPL-3.0)
 * @link      https://github.com/dittertp/ExtDirect
 */

namespace ExtDirect\Collections;

use ExtDirect\ExtDirectResponse;

/**
 * class ResponseCollection
 *
 * @category  ExtDirect
 * @package   ExtDirect
 * @author    Philipp Dittert <philipp.dittert@gmail.com>
 * @copyright 2015 Philipp Dittert
 * @license   http://www.gnu.org/licenses/gpl.txt GNU General Public License, version 3 (GPL-3.0)
 * @link      https://github.com/dittertp/ExtDirect
 */

class ResponseCollection extends Collection implements \IteratorAggregate
{
    /**
     * Add's a ext response instance to the collection
     *
     * @param ExtDirectResponse $response the response instance
     *
     * @return void
     */
    public function add(ExtDirectResponse $response)
    {
            $this->collection[] = $response;
    }

    /**
     * Returns ext direct response as array
     *
     * @return array
     */
    public function asArray()
    {

        $result = array();

        /** @var ExtDirectResponse $response */
        foreach ($this->collection as $response) {
            $result[] = $response->getResultAsArray();
        }

        return $result;
    }

    /**
     * Returns response as json
     *
     * @return string
     */
    public function asJson()
    {
        return json_encode($this->asArray());
    }
}


<?php
/**
 * Phossa Project
 *
 * PHP version 5.4
 *
 * @category  Library
 * @package   Phossa2\Shared
 * @copyright Copyright (c) 2016 phossa.com
 * @license   http://mit-license.org/ MIT License
 * @link      http://www.phossa.com/
 */
/*# declare(strict_types=1); */

namespace Phossa2\Shared\Message\Formatter;

/**
 * Simple HTML formatter for message package
 *
 * @package Phossa2\Shared
 * @author  Hong Zhang <phossa@126.com>
 * @see     DefaultFormatter
 * @version 2.0.0
 * @since   2.0.0 added
 */
class HtmlFormatter extends DefaultFormatter
{
    /**
     * opening tag
     *
     * @var    string
     * @access protected
     */
    protected $openTag = '';

    /**
     * closing tag
     *
     * @var    string
     * @access protected
     */
    protected $closeTag = '';

    /**
     * constructor
     *
     * @param  string $openTag opening tag
     * @param  string $closeTag closing tag
     * @access public
     */
    public function __construct(
        /*# string */ $openTag = '',
        /*# string */ $closeTag = ''
    ) {
        if ($openTag) {
            $this->openTag = $openTag;
        }
        if ($closeTag) {
            $this->closeTag = $closeTag;
        }
    }

    /**
     * {@inheritDoc}
     */
    public function formatMessage(
        /*# string */ $template,
        array $arguments = []
    )/*# : string */ {
        $this->stringize($arguments)->matchTemplate($template, $arguments);
        return vsprintf(
            $this->openTag .
            str_replace('%s', '%s', $template) .
            $this->closeTag,
            $arguments
        );
    }
}


<?php

/*
 * This file is part of the Worker package.
 *
 * (c) EXSyst
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace EXSyst\Component\Worker\Internal;

use EXSyst\Component\Worker\Exception;

final class SocketFactory
{
    private function __construct()
    {
    }

    /**
     * @param string        $socketAddress
     * @param resource|null $socketContext
     *
     * @throws Exception\BindOrListenException
     *
     * @return resource
     */
    private static function doCreateServerSocket($socketAddress, $socketContext = null)
    {
        set_error_handler(null);
        if ($socketContext !== null) {
            $socket = @stream_socket_server($socketAddress, $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $socketContext);
        } else {
            $socket = @stream_socket_server($socketAddress, $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);
        }
        restore_error_handler();
        if ($socket === false) {
            throw new Exception\BindOrListenException($errstr, $errno);
        }

        return $socket;
    }

    /**
     * @param string        $socketAddress
     * @param resource|null $socketContext
     *
     * @throws Exception\BindOrListenException
     *
     * @return resource
     */
    public static function createServerSocket($socketAddress, $socketContext = null)
    {
        try {
            return self::doCreateServerSocket($socketAddress, $socketContext);
        } catch (Exception\BindOrListenException $e) {
            if (($socketFile = IdentificationHelper::getSocketFile($socketAddress)) !== null) {
                try {
                    fclose(self::createClientSocket($socketAddress, 1, $socketContext));
                    // Really in use
                    throw $e;
                } catch (Exception\ConnectException $e2) {
                    // False positive due to a residual socket file
                    unlink($socketFile);

                    return self::doCreateServerSocket($socketAddress, $socketContext);
                }
            } else {
                throw $e;
            }
        }
    }

    /**
     * @param string        $socketAddress
     * @param int|null      $timeout
     * @param resource|null $socketContext
     *
     * @throws Exception\ConnectException
     *
     * @return resource
     */
    public static function createClientSocket($socketAddress, $timeout = null, $socketContext = null)
    {
        if ($timeout === null) {
            $timeout = intval(ini_get('default_socket_timeout'));
        }
        set_error_handler(null);
        if ($socketContext !== null) {
            $socket = @stream_socket_client($socketAddress, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $socketContext);
        } else {
            $socket = @stream_socket_client($socketAddress, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT);
        }
        restore_error_handler();
        if ($socket === false) {
            throw new Exception\ConnectException($errstr, $errno);
        }

        return $socket;
    }
}


<?php
namespace Cora;

class Collection implements \Serializable, \IteratorAggregate, \Countable, \ArrayAccess
{
    ////////////////////////////////////////////////////////////////////////
    //  DATA MEMBERS
    ////////////////////////////////////////////////////////////////////////

    // If this Container has a parent, hold a reference to it here.
    protected $parent;

    // Closure resources.
    protected $signature;

    // If there's a resource that is defined as a closure but after that closure is executed,
    // you want the result stored as a singleton and returned for any subsequence calls...
    // then the named property needs to be set to boolean in this object so the Container knows to
    // save the result as a singleton.
    protected $signaturesToSingletons;

    // Non-closure resources. The reason this is stored separate from Signatures is that you may have a resource
    // that you want to remain in closure form until needed, then store the created resource so subsequent calls
    // return the Singleton version.
    protected $singleton;

    // Combined contents. This stores the combined resources of both $signature and $singleton objects.
    // When a resource is added or removed, this will get recalculated.
    public $content;
    protected $contentKeys;
    protected $size = 0;

    // If the contents of this container get modified, set to true so that any subsequent calls to iterate over
    // or sort the contents recalculate what's in the $content data member.
    protected $contentModified = false;

    // Normally closures are resolved when the resource is asked for. If you want the actual Closure returned,
    // set this to true.
    protected $returnClosure;

    // Sort direction and key
    protected $sortDirection = false;
    protected $sortKey = false;

    ////////////////////////////////////////////////////////////////////////
    //  MAGIC METHODS
    ////////////////////////////////////////////////////////////////////////

    /**
     *  Constructor
     *
     *  @param parent If this container/collection has a parent container.
     *  @param data Data to be loaded into this collection in the form of an object or array.
     *  @param dataKey A key or property on the data being passed in. Will be access $name.
     *  @param returnClosure Whether Closures stored should be returned as Closures or executed and result returned.
     */
    public function __construct($data = false, $dataKey = false, $parent = false, $returnClosure = false)
    {
        $this->parent = $parent;
        $this->signature = new \stdClass();
        $this->singleton = new \stdClass();
        $this->signaturesToSingletons = new \stdClass();;

        // If returnClosure is set to true, then fetching resources through __get will not resolve
        // the closure automatically.
        $this->returnClosure = $returnClosure;

        // If data was passed in, then store it.
        if ($data != false && (is_array($data) || $data instanceof \Traversable)) {
            foreach ($data as $k => $item) {
                // If the array/collection passed in has non-numeric offsets (is associative array or the like)
                // then reuse that offset in this collection assuming a desired datakey wasn't given.
                if (is_numeric($k) == false && $dataKey == false) {
                    $this->add($item, $k, $dataKey, true);
                } else {
                    $this->add($item, false, $dataKey, true);
                }
            }
        }
        $this->generateContent();
    }

    /**
     *  Determines if a resource exists.
     *
     *  @param name The name of the resource sought.
     *  @return Boolean
     */
    public function __isset($name)
    {
      $value = $this->find($name);
      if ($value !== null && !($value instanceof \Exception)) {
          return true;
      }
      return false;
    }

    /**
     *  Returns a resource. If the resource is an object (from Singleton array)
     *  then just returns that object. If resource is defined in a Closure,
     *  then normally executes the Closure and returns the resource within unless
     *  returnClosure is true.
     *
     *  @param name A name in the form of a string or numeric offset.
     *  @return Mixed A resource or null.
     */
    public function __get($name)
    {
        // Grab the resource.
        $resource = $this->find($name);

        // Is Closure
        if ($resource instanceof \Closure) {
            if ($this->returnClosure == false) {
                // Create a resource from the closure.
                $item = $resource($this);

                // If the closure is marked as needing to be saved as a singleton, store result.
                $this->checkIfSingleton($name, $item);

                // Return the resource
                return $item;
            }
            else {
                // Return closure
                return $resource;
            }
        }

        // If Object/Array/primitive
        else {
            return $resource;
        }
    }

    /**
     *  Allows assigning a value to a resource identifier.
     *
     *  @param name A resource identifier (string).
     *  @param value The resource value.
     *  @return Void
     */
    public function __set($name, $value)
    {
        // If this resource was not already set, increase size variable.
        if (!$this->__isset($name)) {
            $this->size += 1;
        }

        if ($value instanceof \Closure) {
            $this->signature->$name = $value;
        }
        else {
            $this->singleton->$name = $value;
        }
        $this->contentModified = true;
    }

    /**
     *  Intercepts methods calls on this object.
     *  $Container->name(arg1, arg2)
     *  $name is passed to make() method to get callback for resource.
     *  The arguments are then passed onwards to the callback.
     *
     *  A good example is when using this class to do dependency injection:
     *  $container->comments = function($c) {
     *      return $c->repository('Comment');
     *  };
     *
     *  When calling $c->repository(args) the resource denoted by the name "repository"
     *  needs to be grabbed and then the arguments passed to that resource.
     *
     *  @param name The name of a resource within the Container. Should be callable.
     *  @param arguments The arguments that will be passed to that Resource.
     *  @return The result of the executed resource or null.
     */
    public function __call($name, $arguments)
    {
        // Grab the callback for the specified name.
        $resource = call_user_func_array(array($this, 'find'), array($name, false, true));

        if ($resource instanceof \Exception) {
          throw $resource;
        }

        // If received a callback
        if ($resource != false && $resource instanceof \Closure) {
            // Add container reference as first argument.
            array_unshift($arguments, $this);

            // Call the callback with the provided arguments.
            $item = call_user_func_array($resource, $arguments);

            // If the closure is marked as needing to be saved as a singleton, store result.
            $this->checkIfSingleton($name, $item);

            return $item;
        }

        // If received some other type of resource back...
        else if ($resource) {
            return $resource;
        }
        return null;
    }

    ////////////////////////////////////////////////////////////////////////
    //  IMPORANT UTILITY FUNCTIONS
    ////////////////////////////////////////////////////////////////////////

    /**
     *  Finds a resource.
     *  If given a numeric value, will grab that offset from the master resource
     *  variable $this->content. If given a string as a name, will look for matching singletons
     *  first, then matching closures second. If given a string, and no match is found, it will
     *  also look in any parent container.
     *
     *  @param name An int or string that denotes a resource.
     *  @param container A parent container.
     *  @return A resource or NULL.
     */
    public function find($name, $container = false, $exceptionOnNoMatch = false)
    {
        // Handle if recursive call or not.
        if (!$container) {
            $container = $this;
        }

        // Do any conversions on the resource name passed in, then check if numeric.
        // I.E. "off2" will get returned as simply numeric 2.
        $name = $this->getName($name);
        if (is_numeric($name)) {
            return $this->fetchOffset($name);
        }

        // If a single object is meant to be returned.
        if (isset($container->singleton->$name)) {
            return $container->singleton->$name;
        }

        // else look for a Closure.
        elseif (isset($container->signature->$name)) {
            return $container->signature->$name;
        }

        // Else check any parents.
        elseif ($container->parent) {
            return $container->find($name, $container->parent);
        }

        if ($exceptionOnNoMatch) {
          return new \Exception("No such resource ('$name') exists within this collection");
        }
        return null;
    }

    /**
     *  Adds a resource to this Container.
     *
     *  @param item The item to be added. Can be a primitive, array, object, or Closure.
     *  @param key The key (identifier) to store this item as.
     *  @param dataKey If given, uses the value stored in that key within the Object or Array as the collection key.
     *  @return self
     */
    public function add($item, $key = false, $dataKey = false)
    {
        // If the data should be sorted by a property/key on it, but you want to add a prefix to
        // the result. Example: If $item->month is a numeric value between 1 and 12, but there may be
        // missing months in the data. Trying to access $collection->{'1'} will fetch OFFSET 1 which if
        // January data is missing, could be another month. No Bueno. By passing in a prefix you can use
        // $collection->month1 which will either be set or not and won't resort to returning first offset as result.
        $keyPrefix = '';
        if (is_array($dataKey)) {
            $keyPrefix = $dataKey[1];
            $dataKey = $dataKey[0];
        }

        if (is_object($item)) {
            if ($key) {
                if (!isset($this->key)) { $this->size += 1; }
                $this->singleton->$key = $item;
            }
            else if ($dataKey && isset($item->$dataKey)) {
                $key = $item->$dataKey;
                if (!isset($this->key)) {
                    $this->size += 1;
                }
                $this->singleton->{$keyPrefix.$key} = $item;
            }
            else {
                $offset = '_item'.$this->size;
                $this->size += 1;
                $this->singleton->{"$offset"} = $item;
            }
        }
        else if (is_array($item)) {
            if ($key) {
                if (!isset($this->key)) { $this->size += 1; }
                $this->singleton->$key = $item;
            }
            else if ($dataKey && isset($item[$dataKey])) {
                $key = $item[$dataKey];
                if (!isset($this->key)) { $this->size += 1; }
                $this->singleton->{$keyPrefix.$key} = $item;
            }
            else {
                $offset = '_item'.$this->size;
                $this->size += 1;
                $this->singleton->{"$offset"} = $item;
            }
        }
        else {
            if ($key) {
                if (!$this->__isset($key)) { $this->size += 1; }
                $this->singleton->$key = $item;
            }
            else {
                $offset = '_item'.$this->size;
                $this->size += 1;
                $this->singleton->{"$offset"} = $item;
            }
        }
        $this->contentModified = true;
        return $this;
    }

    /**
     *  Remove a resource.
     *
     *  @param name The identifier (key) for this resource within the Container.
     *  @return Void.
     */
    public function delete($name)
    {
        // Get actual name. If "off0" translates to just 0.
        $name = $this->getName($name);

        // Figure out the key of the object we want to delete.
        // (if numeric value was passed in, turn that into actual key)
        $resourceKey = $name;
        if (is_numeric($name)) {
            $resourceKey = $this->fetchOffsetKey($name);
        }

        // Only mark the content as modified and change count if the delete call found
        // a resource to remove.
        if ($this->processDelete($resourceKey)) {
            $this->contentModified = true;
            $this->size -= 1;
        }
    }

    /**
     *  Alias for Delete().
     */
    public function remove($name)
    {
        $this->delete($name);
    }

    /**
     *  Handles deleting a resource.
     *
     *  @param name The name of the resource to be deleted.
     *  @param container A parent container which will also be searched for item to remove.
     */
    public function processDelete($name, $container = false)
    {
        // Handle if recursive call or not.
        if (!$container) {
            $container = $this;
        }

        // If a single object is meant to be returned.
        if (isset($container->singleton->$name)) {
            unset($container->singleton->$name);
            return true;
        }

        // else look for a Closure.
        if (isset($container->signature->$name)) {
            unset($container->signature->$name);
            return true;
        }

        // Else check any parents.
        elseif ($container->parent) {
            return $container->processDelete($name, $container->parent);
        }
        return false;
    }

    /**
     *  Stores a single version of a resource created by a closure so that subsequent requests
     *  are given the already created resource instead of invoking the closure again.
     *  This method can be given resources which are not closures, but doesn't do anything useful
     *  unless given a closure.
     *
     *  @param $name A string which starts with a non-numeric character.
     *  @param $value A closure which returns an object, array, or primitive when invoked.
     *  @return void
     */
    public function singleton($name, $value)
    {
        // If value is a closure, store a reference that tells us we need to store the resulting
        // value as a singleton after it's first executed.
        if ($value instanceOf \Closure) {
            $this->signaturesToSingletons->$name = true;
        }
        // Use the __set magic method to handle setting the resource.
        $this->$name = $value;
    }

    /**
     *  Checks if a resource should be saved as a singleton. Save item if yes.
     *
     *  @param string $name The name of the resource.
     *  @param mixed $item An instance of the resource.
     *  @return void
     */
    public function checkIfSingleton($name, $item)
    {
        // If the closure is marked as needing to be saved as a singleton, store result.
        if (isset($this->signaturesToSingletons->$name) and $this->signaturesToSingletons->$name) {
            $this->$name = $item;
            $this->signaturesToSingletons = false;
        }
    }

    /**
     *  Returns a resource by numerical offset.
     *
     *  @param num A number.
     */
    public function fetchOffset($num)
    {
        // If the content has been modified, regenerate $this->content.
        // and $this->contentKeys.
        if ($this->contentModified) {
            $this->generateContent();
        }

        // If the offset exists, return the data.
        $key = $this->fetchOffsetKey($num);
        if ($key != null) {
            return $this->content[$key];
        }

        return null;
    }

    /**
     *  Returns the offset name (string) of a resource for a given numerical offset.
     *
     *  @param num A numerical offset.
     */
    public function fetchOffsetKey($num)
    {
        // Keys for resources are always strings. If given a numerical offset,
        // that offset needs to be interpreted to determine the actual property's name
        // at that offset.
        return isset($this->contentKeys[$num]) ? $this->contentKeys[$num] : null;
    }

    /**
     *  Attempts to return the collection as a simple array.
     *
     *  @return array
     */
    public function toArray()
    {
        $collection = $this->getIterator();
        $plainArray = [];

        foreach($collection as $prop => $result) {
            if (is_object($result) && method_exists($result, 'toArray')) {
                $plainArray[] = $result->toArray();
            } else {
                $plainArray[] = $result;
            }
        }
        return $plainArray;
    }

    /**
     *  Attempts to return the collection as a JSON encoded string.
     *
     *  @return string
     */
    public function toJson()
    {
        return json_encode($this->toArray());
    }

    ////////////////////////////////////////////////////////////////////////
    //  UTILITY SUMMATION AND SINGLE RETURN METHODS
    ////////////////////////////////////////////////////////////////////////

    /**
     *  Returns the number of resources stored in this container/collection.
     *
     *  @param includeParents Include parent resources in the total count?
     *  @param recount If true, then recounts the items rather than returning the stored count.
     *  @return The number of resources stored in this Container.
     */
    public function count($includeParents = false, $recount = false)
    {
        if ($recount) {
            return $this->getIterator()->count();
        }
        return $this->size;
    }

    /**
     *  Returns the FIRST result with a matching key=>value, else NULL.
     *  Example: if objects representing Users are stored and you want the result with
     *  an Email value of 'Bob@gmail.com'
     *
     *  @param key A string representing a key or property.
     *  @param value A value for which you are searching.
     */
    public function getByValue($key, $value)
    {
        $collection = $this->getIterator();

        foreach($collection as $result) {
            if ($result->$key == $value) {
                return $result;
            }
        }
        return null;
    }

    /**
     *  Returns the sum of the contents of the Container.
     *  If given a key, will sum that $key->value.
     *  Obviously it's important to have some consistency in the types of resources you
     *  are storing or else the results might not make much sense or could crash.
     *
     *  @param key A key on which to sum the when looking at the contents of this Container.
     *  @return A number.
     */
    public function sumByKey($key = false)
    {
        $collection = $this->getIterator();
        $sum = 0;
        foreach ($collection as $result) {
            if ($key && isset($result->$key)) {
                $sum += $result->$key;
            }
            else if ($key && isset($result[$key])) {
                $sum += $result[$key];
            }
            else {
                $sum += $result;
            }
        }
        return $sum;
    }

    /**
     *  Returns the largest value.
     *
     *  @param key A key on which to sum the when looking at the contents of this Container.
     *  @return mixed Value
     */
    public function max($key = false)
    {
        $collection = $this->getIterator();
        $max = 0;
        $valueToReturn = 0;
        foreach ($collection as $result) {
            if ($key && isset($result->$key)) {
                if ($result->$key > $max) {
                    $max = $result->$key;
                    $valueToReturn = $result;
                }
            }
            else if ($key && isset($result[$key])) {
                if ($result[$key] > $max) {
                    $max = $result[$key];
                    $valueToReturn = $result;
                }
            }
            else {
                if ($result > $max) {
                    $max = $result;
                    $valueToReturn = $result;
                }
            }
        }
        return $valueToReturn;
    }

    /**
     *  Returns the smallest value.
     *
     *  @param key A key on which to sum the when looking at the contents of this Container.
     *  @return mixed Value
     */
    public function min($key = false)
    {
        $sortedCollection = $this->sort($key, 'desc');
        return $sortedCollection->get(0);
    }

    ////////////////////////////////////////////////////////////////////////
    //  DATA FILTERING AND MANIPULATION (Returns instance of Container)
    ////////////////////////////////////////////////////////////////////////

    /**
     *  Returns a SUBSET of the results.
     *  If no matching subset exists, returns an empty Container.
     *
     *  Example would be if bunch of User objects were stored in this Container and you
     *  wanted to return a subset of Users who's "Type" is equal to "Admin".
     *  Container->where('Type', 'Admin');
     *
     *  @param key A key to look at when comparing the operator and the desired value.
     *  @param desiredValue The value to compare against.
     *  @param op The operator used in comparision.
     *  @return A Container with the matching resources.
     */
    public function where($key = false, $desiredValue, $op = "==")
    {
        $collection = $this->getIterator();
        $subset = new Collection();

        foreach($collection as $prop => $result) {
            // Grab resource value
            $realValue = $result;
            if (is_object($result)) {
                $realValue = $result->$key;
            }
            else if (is_array($result)) {
                $realValue = $result[$key];
            }

            // Check value against operator given
            $add = false;
            if ($op == '==' && $realValue == $desiredValue) {
                $add = true;
            }
            else if ($op == '>=' && $realValue >= $desiredValue) {
                $add = true;
            }
            else if ($op == '<=' && $realValue <= $desiredValue) {
                $add = true;
            }
            else if ($op == '>' && $realValue > $desiredValue) {
                $add = true;
            }
            else if ($op == '<' && $realValue < $desiredValue) {
                $add = true;
            }
            else if ($op == '===' && $realValue === $desiredValue) {
                $add = true;
            }
            else if ($op == '!=' && $realValue != $desiredValue) {
                $add = true;
            }

            // Add the resource to the subset if valid
            if ($add) {
                $subset->add($result, $prop);
            }
        }
        return $subset;
    }

    /**
     *  Merges the data given into this Container.
     *
     *  @param data The data to merge in.
     *  @param key The key name to sore the data in (should only be used when passing a single resource in)
     *  @param dataKey The value on the object/array to use as a key.
     *  @return self
     */
    public function merge($data, $key = false, $dataKey = false)
    {
        if ($data != false && (is_array($data) || is_object($data))) {
            foreach ($data as $item) {
                $this->add($item, $key, $dataKey, true);
            }
        }
        else {
            $this->add($data, $key, $dataKey);
        }
        return $this;
    }

    /**
     *  Sorts the contents of this Container.
     *
     *  @param key The key on which to sort.
     *  @param dir The sort direction.
     *  @return A reference to this Container.
     */
    public function sort($metric = false, $preserveKeys = true, $descending = false, $options = SORT_REGULAR)
    {
        if ($this->contentModified) {
            $this->generateContent();
        }

        $results = [];
        $values = [];
        foreach ($this->content as $key => $item) {
            $value = $this->getValue($item, $metric);
            $values[$key] = $value;
        }

        $descending ? arsort($values, $options) : asort($values, $options);

        if ($preserveKeys) {
            foreach (array_keys($values) as $key) {
                $results[$key] = $this->content[$key];
            }
        } else {
            foreach (array_keys($values) as $key) {
                $results[] = $this->content[$key];
            }
        }

        return new self($results);
    }

   /**
    *  groupBy
    *
    *  @param callback A callable function.
    *  @return A collection
    */
    public function groupBy($metric)
    {
        $collection = $this->getIterator();

        // Create a new collection which we'll populate with our grouped data
        $GroupedCollection = new self();

        foreach($collection as $key => $item) {

            // Determine the Group this item belongs in.
            if ($metric instanceof \Closure) {
                $group = $metric($item);
            } else {
                $group = $this->getValue($item, $metric);
            }

            if (is_numeric($group)) {
                $group = $metric.$group;
            }

            // Check if this group already exists, or else create it.
            if (!isset($GroupedCollection->$group)) {
                $GroupedCollection->$group = new self();
            }
            $GroupedCollection->$group->add($item);
        }
        return $GroupedCollection;
    }

    /**
     *  Map callback to data.
     *
     *  @param callback A callable function.
     *  @return A collection
     */
    public function map($callback, $data = false)
    {
      $collection = $this->getIterator();
      $mutatedCollection = new Collection();

      foreach($collection as $prop => $result) {
        // Prep data to pass to closure
        $funcArgs = is_array($data) ? $data : [$data];
        array_unshift($funcArgs, $prop);
        array_unshift($funcArgs, $result);

        $aValue = call_user_func_array($callback, $funcArgs);
        $mutatedCollection->add($aValue);
      }
      return $mutatedCollection;
    }

    /**
     *  Filter data to that which passes the provided callback.
     *
     *  @param callback A callable function.
     *  @return A collection
     */
    public function filter($callback, $data = false)
    {
      $collection = $this->getIterator();
      $mutatedCollection = new Collection();

      foreach($collection as $prop => $result) {
        // Prep data to pass to closure
        $funcArgs = is_array($data) ? $data : [$data];
        array_unshift($funcArgs, $prop);
        array_unshift($funcArgs, $result);

        $aValue = call_user_func_array($callback, $funcArgs);
        if ($aValue) {
          $mutatedCollection->add($result);
        }
      }
      return $mutatedCollection;
    }

    ////////////////////////////////////////////////////////////////////////
    //  SIMPLE ACCESSORS AND MODIFIERS
    ////////////////////////////////////////////////////////////////////////

    /**
     *  Simple Accessor for $signature data member
     *  Example of usage is to access the resources of a parent container from a child.
     *
     *  @return Object
     */
    public function getSignatures()
    {
        return $this->signature;
    }

    /**
     *  Simple Accessor for $singleton data member
     *
     *  @return Object
     */
    public function getSingletons()
    {
        return $this->singleton;
    }

    /**
     *  Simple setter for $returnClosure data member
     *
     *  @return Void
     */
    public function returnClosure($bool)
    {
        $this->returnClosure = $bool;
    }

    /**
     *  Unsets a resource stored in the singleton data member.
     *
     *  @return Void
     */
    public function unsetSingleton($name)
    {
        $this->singleton->$name = false;
        $this->contentModified = true;
    }

    /**
     *  Unsets a Closure stored in the signature data member.
     *
     *  @return Void
     */
    public function unsetSignature($name)
    {
        $this->signature->$name = false;
        $this->contentModified = true;
    }

    ////////////////////////////////////////////////////////////////////////
    //  REQUIRED BY PSR-11.
    //  https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md
    //
    //  Note that I'm not fully implementing the spec as I don't want to throw their exceptions.
    ////////////////////////////////////////////////////////////////////////

    /**
     *  Alias of magic method __get()
     *
     *  @param $name Int | String
     *  @return Mixed
     */
    public function get($name)
    {
        return $this->$name;
    }

    /**
     *  Alias of magic method __isset()
     *
     *  @param $name Int | String
     *  @return Boolean
     */
    public function has($name)
    {
        return isset($this->$name);
    }

    ////////////////////////////////////////////////////////////////////////
    //  REQUIRED BY IteratorAggregate INTERFACE
    ////////////////////////////////////////////////////////////////////////

    /**
     *  Returns an ArrayIterator for traversing the contents of the Container.
     *
     *  @return ArrayIterator
     */
    public function getIterator() {
        if (!$this->content || $this->contentModified) {
            $this->generateContent();
        }
        return new \ArrayIterator($this->content);
    }

    ////////////////////////////////////////////////////////////////////////
    //  REQUIRED BY ArrayAccess INTERFACE
    ////////////////////////////////////////////////////////////////////////

    /**
     *  Checks if an offset is set. An offset can be a numeric number or key name (string).
     *
     *  @param $offset Int | String
     *  @return bool
     */
    public function offsetExists($offset)
    {
        if ($this->get($offset)) {
            return true;
        }
        return false;
    }

    /**
     *  Returns an offset. If offset is not set, then returns null.
     *
     *  @param $offset Int | String
     *  @return mixed
     */
    public function offsetGet($offset)
    {
        return $this->$offset;
    }

    /**
     *  Assigns the value provided to the offset.
     *
     *  @param $offset Int | String
     *  @param $value Mixed
     *  @return Void
     */
    public function offsetSet($offset, $value)
    {
        $this->$offset = $value;
    }

    /**
     *  Unsets the given offset
     *
     *  @param $offset Int | String
     *  @return Void
     */
    public function offsetUnset($offset)
    {
        $this->delete($offset);
    }

    ////////////////////////////////////////////////////////////////////////
    //  REQUIRED BY PHPUNIT (because it tries to serialize containers and Closures can't be serialized)
    ////////////////////////////////////////////////////////////////////////

    public function serialize()
    {
        return serialize($this->singleton);
    }

    public function unserialize($data)
    {
        return unserialize($data);
    }

    ////////////////////////////////////////////////////////////////////////
    //  Non-public Methods
    ////////////////////////////////////////////////////////////////////////

    /**
     *  Merges the $signature and $singleton resources together into a single result stored in $content.
     *
     *  @return null
     */
    protected function generateContent()
    {
        $this->content = array_merge_recursive((array) $this->signature, (array) $this->singleton);
        $this->contentKeys = array_keys($this->content);
        $this->contentModified = false;
    }

    /**
    *  Returns the value when given a piece of data.
    *  If the data item is a primitive or if no key was given, then the item
    *  is simply returned. However, if the data item is an object or array
    *  and a key was given, then returns the offset given by the key as a value.
    *
    *  @param $data Mixed
    *  @param $key Int | String
    *  @return mixed
    */
    protected function getValue($data, $key = false)
    {
        $returnValue = $data;
        if ($key && is_object($data)) {
            $returnValue = $data->$key;
        }
        else if ($key && is_array($data)) {
            $returnValue = $data[$key];
        }
        return $returnValue;
    }

    /**
     *  Find the name/offset of a resource.
     *  If trying access resource "off0" this should grab the first
     *  resource stored. Which means removing the "off" part and making
     *  the name of the resource sought simply the number 0.
     *
     *  @param $name The designed resource. A name or offset.
     *  @return Mixed
     */
    protected function getName($name)
    {
        preg_match("/off([0-9]+)/", $name, $nameMatch);
        if (isset($nameMatch[1])) {
            $name = (int) $nameMatch[1];
        }
        return $name;
    }

    /**
     *  DEPRECIATED STUFF. REMOVE WHEN CONFIDENT IT'S SAFE
     */

    /**
     *  Sorts the contents of this Container.
     *
     *  @param key The key on which to sort.
     *  @param dir The sort direction.
     *  @return A reference to this Container.
     */
     public function sort1($key = false, $dir = 'desc')
     {
         if ($this->contentModified) {
             $this->generateContent();
         }
         $this->sortDirection = $dir;
         $this->sortKey = $key;
         $this->mergesort($this->content, array($this, 'compare'));
         $this->contentKeys = array_keys($this->content);
         return $this;
     }

    /**
     *  A stable implementation of Mergesort (aka Stable-sort).
     *  The end result is the array passed in being sorted according to the strategy provided
     *  by the comparison function passed in.
     *
     *  @param $array Array
     *  @param $comparisionFunction A Callable.
     */
    protected function mergesort(&$array, $comparisonFunction) {

        // Exit right away if only zero or one item.
        if (count($array) < 2) {
            return true;
        }

        // Cut results in half.
        $halfway    = count($array) / 2;
        $leftArray  = array_slice($array, 0, $halfway, true);
        $rightArray = array_slice($array, $halfway, null, true);

        // Recursively call sort on left and right pieces
        $this->mergesort($leftArray, $comparisonFunction);
        $this->mergesort($rightArray, $comparisonFunction);

        // Check if the last element of the first array is less than the first element of 2nd.
        // If so, we are done. Just put the two arrays together for final result.
        if (call_user_func($comparisonFunction, end($leftArray), reset($rightArray)) < 1) {
            $array = $leftArray + $rightArray;
            return true;
        }

        // Set result array to blank. Set pointers to beginning of pieces.
        $array = array();
        reset($leftArray);
        reset($rightArray);

        // While looking at the current element in each array...
        while (current($leftArray) && current($rightArray)) {

            // Add the lowest element between the current element in the left and right arrays to the result.
            // Then advance to the next item on that side.
            if (call_user_func($comparisonFunction, current($leftArray), current($rightArray)) < 1) {
                $array[key($leftArray)] = current($leftArray);
                next($leftArray);
            } else {
                $array[key($rightArray)] = current($rightArray);
                next($rightArray);
            }
        }

        // After doing the left and right comparisons above, you may hit the end of the left array
        // before hitting the end of the right (or vice-versa). We need to make sure these left-over
        // elements get added to our results.
        while (current($leftArray)) {
            $array[key($leftArray)] = current($leftArray);
            next($leftArray);
        }
        while (current($rightArray)) {
            $array[key($rightArray)] = current($rightArray);
            next($rightArray);
        }
        return true;
    }

    /**
    *  A simple compare function which is used by the Sort method.
    *
    *  @param $a Mixed
    *  @param $b Mixed
    *  @return boolean
    */
     protected function compare($a, $b)
     {
        $key = $this->sortKey;
        $aValue = $this->getValue($a, $key);
        $bValue = $this->getValue($b, $key);
        echo $aValue->format('m/d/Y')." - ".$bValue->format('m/d/Y')." = ";
        if ($aValue == $bValue) {
            echo '0<br>';
            return 0;
        }
        if (strtolower($this->sortDirection) == 'desc') {
            echo (($aValue < $bValue) ? -1 : 1)."<br>";
            return ($aValue < $bValue) ? -1 : 1;
        }
        else {
            echo (($aValue < $bValue) ? 1 : -1)."<br>";
            return ($aValue < $bValue) ? 1 : -1;
        }
     }

    /**
    *  Sorts the contents of this Container.
    *
    *  @param key The key on which to sort.
    *  @param dir The sort direction.
    *  @return A reference to this Container.
    */
    public function sort2($key = false, $dir = 'desc')
    {
        if ($this->contentModified) {
            $this->generateContent();
        }
        $this->sortDirection = $dir;
        $this->sortKey = $key;
        $temp = $this->msort(array_values($this->content));
        return new self($temp);
    }

    public function msort($data) {
        $numItems = count($data);

        // Stop if we are down to one item
        if($numItems>1) {

            // Find middle point of array so we can split there
            $midPoint = round($numItems/2, 0, PHP_ROUND_HALF_DOWN);

            // Recursive call
            $left   = $this->msort(array_slice($data, 0, $midPoint));
            $right  = $this->msort(array_slice($data, $midPoint, $numItems));

            // Init offset vars
            $i = $j = $k = 0;

            // Calculate the size of the split arrays
            $leftSize       = count($left);
            $rightSize      = count($right);

            // While both the left and right array still have items...
            while ($i < $leftSize && $j < $rightSize) {
                if ($this->compare2($left[$i], $right[$j], true) < 1) {
                    $data[$k] = $left[$i];
                    $i++;
                } else {
                    $data[$k] = $right[$j];
                    $j++;
                }
                $k++;
            }

            // If the left side still had items remaining (I.E. because it was larger)
            while ($i < $leftSize) {
                $data[$k] = $left[$i];
                $i++;
                $k++;
            }

            // If the right side still had items remaining (I.E. because it was larger)
            while ($j < $rightSize) {
                $data[$k] = $right[$j];
                $j++;
                $k++;
            }
        }
        return $data;
    }

   /**
    *  A simple compare function which is used by the Sort method.
    *
    *  @param $a Mixed
    *  @param $b Mixed
    *  @return boolean
    */
    protected function compare2($a, $b, $descending = false)
    {
        $key = $this->sortKey;
        $aValue = $this->getValue($a, $key);
        $bValue = $this->getValue($b, $key);
        echo $aValue->format('m/d/Y')." - ".$bValue->format('m/d/Y')." - ";
        if ($aValue == $bValue) {
            return 0;
        }
        if ($descending == true) {
            echo (($aValue > $bValue) ? -1 : 1)."<br>";
            return ($aValue > $bValue) ? -1 : 1;
        }
        else {
            echo (($aValue > $bValue) ? 1 : -1)."<br>";
            return ($aValue > $bValue) ? 1 : -1;
        }
    }

} // END Container

function my_print_r ($a) {
    $keys = '';
    foreach(array_keys($a) as $key)
    $keys .= "$key, ";

    return $keys;
  }


<?php
/**
 * Copyright (c) 2018 Wakers.cz
 *
 * @author Jiří Zapletal (http://www.wakers.cz, zapletal@wakers.cz)
 *
 */

namespace Wakers\StructureModule\Component\Frontend\RecipeSlugRemoveModal;

use Wakers\BaseModule\Component\Frontend\BaseControl;
use Wakers\StructureModule\Database\RecipeSlug;
use Wakers\StructureModule\Manager\RecipeSlugManager;
use Wakers\StructureModule\Repository\RecipeSlugRepository;
use Wakers\StructureModule\Security\StructureAuthorizator;

class RecipeSlugRemoveModal extends BaseControl
{
    /**
     * @var RecipeSlugRepository
     */
    protected $recipeSlugRepository;

    /**
     * @var RecipeSlugManager
     */
    protected $recipeSlugManager;

    /**
     * Entita načtena při otevření modálního okna
     * @var RecipeSlug
     */
    protected $recipeSlugEntity;

    /**
     * @var callable
     */
    public $onRemove = [];

    /**
     * @var callable
     */
    public $onOpen = [];

    /**
     * RecipeSlugRemoveModal constructor.
     * @param RecipeSlugRepository $recipeSlugRepository
     * @param RecipeSlugManager $recipeSlugManager
     */
    public function __construct(RecipeSlugRepository $recipeSlugRepository, RecipeSlugManager $recipeSlugManager)
    {
        $this->recipeSlugRepository = $recipeSlugRepository;
        $this->recipeSlugManager = $recipeSlugManager;
    }

    /**
     * Render
     */
    public function render() : void
    {
        $this->template->recipeSlug = $this->recipeSlugEntity;
        $this->template->setFile(__DIR__.'/templates/recipeSlugRemoveModal.latte');
        $this->template->render();
    }

    /**
     * Handler pro otevření modálního okna
     * @param int $id
     */
    public function handleOpen(int $id) : void
    {
        if ($this->presenter->isAjax())
        {
            $this->recipeSlugEntity = $this->recipeSlugRepository->findOneById($id);

            $this->presenter->handleModalToggle('show', '#wakers_structure_recipe_slug_remove_modal', FALSE);
            $this->onOpen();
        }
    }

    /**
     * Handler pro odstranění
     * @param int $id
     * @throws \Exception
     */
    public function handleRemove(int $id) : void
    {
        if ($this->presenter->isAjax() && $this->presenter->user->isAllowed(StructureAuthorizator::RES_RECIPE_SLUG_REMOVE_MODAL))
        {
            $this->recipeSlugEntity = $this->recipeSlugRepository->findOneById($id);

            $this->recipeSlugManager->delete($this->recipeSlugEntity);

            $this->presenter->notificationAjax(
                'Slug odstraněn',
                "Slug '{$this->recipeSlugEntity->getSlug()}' byl úspěšně odstraněn.",
                'success',
                FALSE
            );

            $this->presenter->handleModalToggle('hide', '#wakers_structure_recipe_slug_remove_modal', FALSE);

            $this->onRemove();
        }
    }
}


<?php
/**
 * Phossa Project
 *
 * PHP version 5.4
 *
 * @category  Library
 * @package   Phossa2\Shared
 * @copyright Copyright (c) 2016 phossa.com
 * @license   http://mit-license.org/ MIT License
 * @link      http://www.phossa.com/
 */
/*# declare(strict_types=1); */

namespace Phossa2\Shared\Delegator;

/**
 * DelegatorTrait
 *
 * Implementation of DelegatorInterface
 *
 * @package Phossa2\Shared
 * @author  Hong Zhang <phossa@126.com>
 * @see     DelegatorInterface
 * @version 2.0.16
 * @since   2.0.8  added
 * @since   2.0.15 modified, moved to new namespace
 * @since   2.0.16 added clearLookupCache()
 */
trait DelegatorTrait
{
    /**
     * lookup pool of registries
     *
     * @var    array
     * @access protected
     */
    protected $lookup_pool = [];

    /**
     * cached found key
     *
     * @var    string
     * @access private
     */
    private $cache_key;

    /**
     * cached lookup registry for found $cache_key
     *
     * @var    object
     * @access private
     */
    private $cache_reg;

    /**
     * Append one registry to lookup pool
     *
     * @param  object $registry
     * @return $this
     * @access protected
     */
    protected function addRegistry($registry)
    {
        // remove it if exists already
        $this->removeFromLookup($registry);

        // set delegator in registry
        if ($registry instanceof DelegatorAwareInterface) {
            $registry->setDelegator($this);
        }

        // append to the pool
        $this->lookup_pool[] = $registry;

        return $this;
    }

    /**
     * check existence in the whole lookup pool
     *
     * @param  string $key
     * @return bool
     * @access protected
     */
    protected function hasInLookup(/*# string */ $key)/*# : bool */
    {
        foreach ($this->lookup_pool as $registry) {
            if ($this->hasInRegistry($registry, $key)) {
                $this->cache_key = $key;
                $this->cache_reg = $registry;
                return true;
            }
        }
        return false;
    }

    /**
     * get from lookup pool, return NULL if not found
     *
     * @param  string $key
     * @return mixed|null
     * @access protected
     */
    protected function getFromLookup(/*# string */ $key)
    {
        // found already ? or try find
        if ($key === $this->cache_key || $this->hasInLookup($key)) {
            return $this->getFromRegistry($this->cache_reg, $key);
        }
        // not found
        return null;
    }

    /**
     * Remove one registry from the pool
     *
     * @param  object $registry
     * @return $this
     * @access protected
     */
    protected function removeFromLookup($registry)
    {
        foreach ($this->lookup_pool as $idx => $reg) {
            if ($registry === $reg) {
                if ($reg instanceof DelegatorAwareInterface) {
                    $reg->setDelegator();
                }
                unset($this->lookup_pool[$idx]);
            }
        }
        return $this;
    }

    /**
     * Clear lookup cache
     *
     * @access protected
     */
    protected function clearLookupCache()
    {
        $this->cache_key = null;
    }

    /**
     * Try HAS in registry
     *
     * @param  object $registry
     * @param  string $key
     * @return bool
     * @access protected
     */
    abstract protected function hasInRegistry(
        $registry,
        /*# string */ $key
    )/*# : bool */;

    /**
     * Try GET from registry
     *
     * @param  object $registry
     * @param  string $key
     * @return mixed|null
     * @access protected
     */
    abstract protected function getFromRegistry(
        $registry,
        /*# string */ $key
    );
}