<?php

declare(strict_types=1);

namespace NAttreid\Orm\Structure;

use InvalidArgumentException;
use NAttreid\Orm\Mapper;
use Nette\DI\Container;
use Nette\SmartObject;
use Nextras\Dbal\Connection;
use Nextras\Dbal\QueryException;
use Nextras\Dbal\Result\Result;
use Nextras\Dbal\Result\Row;
use Nextras\Dbal\Utils\FileImporter;
use Serializable;

/**
 * Tabulka
 *
 * @property-read string $name
 * @property-read Connection $connection
 * @property-read Column[] $columns
 * @property-read PrimaryKey $primaryKey
 * @property-read bool $useCamelCase
 * @property-read string $collate
 * @property-read bool $exists
 *
 * @author Attreid <attreid@gmail.com>
 */
class Table implements Serializable
{
	use SmartObject;

	/**
	 * Add migration function function (\Nextras\Dbal\Result\Row, \Nextras\Dbal\Connection)
	 * @var callable[]
	 */
	public $migration = [];

	/** @var string */
	private $database;

	/** @var string */
	private $name;

	/** @var Connection */
	private $connection;

	/** @var Container */
	private $container;

	/** @var ITableFactory */
	private $tableFactory;

	/** @var string */
	private $engine = 'InnoDB';

	/** @var string */
	private $charset = 'utf8';

	/** @var string */
	private $collate = 'utf8_czech_ci';

	/** @var Column[] */
	private $columns = [];

	/** @var Column[] */
	private $oldColumns = [];

	/** @var PrimaryKey */
	private $primaryKey;

	/** @var Index[] */
	private $keys = [];

	/** @var Constrait[] */
	private $constraints = [];

	/** @var int */
	private $autoIncrement = null;

	/** @var string */
	private $addition = null;

	/** @var Table[] */
	private $relationTables = [];

	/** @var string */
	private $prefix;

	/** @var string */
	private $defaultDataFile;

	public function __construct(string $name, string $prefix, Connection $connection, Container $container, ITableFactory $tableFactory)
	{
		$this->database = $connection->getConfig()['database'];
		$this->name = $name;
		$this->prefix = $prefix;
		$this->connection = $connection;
		$this->container = $container;
		$this->tableFactory = $tableFactory;
	}

	/**
	 * @return string
	 */
	protected function getName(): string
	{
		return $this->name;
	}

	/**
	 * @return Connection
	 */
	protected function getConnection(): Connection
	{
		return $this->connection;
	}

	/**
	 * @return Column[]
	 */
	protected function getColumns(): array
	{
		return $this->columns;
	}

	/**
	 * @return string
	 */
	protected function getCollate(): string
	{
		return $this->collate;
	}

	/**
	 * @return bool
	 * @throws QueryException
	 */
	protected function isExists(): bool
	{
		return $this->exists($this->name);
	}

	/**
	 * @param string $table
	 * @return bool
	 * @throws QueryException
	 */
	public function exists(string $table): bool
	{
		$result = $this->connection->query("SHOW TABLES LIKE %s", $table)->fetch();
		return $result ? true : false;
	}

	/**
	 * Nastavi engine (default=InnoDB)
	 * @param string $engine
	 * @return self
	 */
	public function setEngine(string $engine): self
	{
		$this->engine = $engine;
		return $this;
	}

	/**
	 * Nastavi charset (default=utf8)
	 * @param string $charset
	 * @return self
	 */
	public function setCharset(string $charset): self
	{
		$this->charset = $charset;
		return $this;
	}

	/**
	 * Nastavi engine (default=utf8_czech_ci)
	 * @param string $collate
	 * @return self
	 */
	public function setCollate(string $collate): self
	{
		$this->collate = $collate;
		return $this;
	}

	/**
	 * Nastavi soubor pro nahrani dat pri vytvareni tabulky
	 * @param string $file
	 */
	public function setDefaultDataFile(string $file)
	{
		$this->defaultDataFile = $file;
	}

	/**
	 * Vytvori spojovou tabulku
	 * @param string|Table $tableName
	 * @return self
	 */
	public function createRelationTable($tableName): self
	{
		$table = $this->getTableData($tableName);

		$name = $this->name . '_x_' . $table->name;

		return $this->relationTables[] = $this->tableFactory->create($name, $this->prefix);
	}

	/**
	 * Zmena nazvu sloupce
	 * @param string $name
	 * @param Column $column
	 * @internal
	 * @return self
	 */
	public function addColumnToRename(string $name, Column $column): self
	{
		$this->oldColumns[$name] = $column;
		return $this;
	}

	/**
	 * Proveri zda tabulka existuje a podle toho ji bud vytvori nebo upravi (pokud je treba)
	 * @return bool pokud je vytvorena vrati true
	 * @throws QueryException
	 */
	public function check(): bool
	{
		$isNew = false;
		$this->connection->query('SET foreign_key_checks = 0');
		if (!$this->exists) {
			$this->create();
			$isNew = true;
			if ($this->defaultDataFile !== null) {
				FileImporter::executeFile($this->connection, $this->defaultDataFile);
			}

			foreach ($this->relationTables as $table) {
				$table->check();
			}
		} else {
			$this->changeColumns();
			$this->modifyColumnsAndKeys();
			$this->modifyTable();
		}
		$this->connection->query('SET foreign_key_checks = 1');
		return $isNew;
	}

	/**
	 * Vytvori tabulku
	 * @throws QueryException
	 */
	private function create(): void
	{
		$query = "CREATE TABLE IF NOT EXISTS %table (\n"
			. implode(",\n", $this->columns) . ",\n"
			. ($this->primaryKey !== null ? $this->primaryKey . (empty($this->keys) ? '' : ",\n") : '')
			. implode(",\n", $this->keys) . (empty($this->constraints) ? '' : ",\n")
			. implode(",\n", $this->constraints)
			. "\n) ENGINE=$this->engine" . (empty($this->autoIncrement) ? '' : " AUTO_INCREMENT=$this->autoIncrement") . " DEFAULT CHARSET=$this->charset COLLATE=$this->collate"
			. (empty($this->addition) ? '' : "/*$this->addition*/");

		$this->connection->query($query, $this->name);
	}

	/**
	 * Upravi tabulku
	 * @throws QueryException
	 */
	private function modifyTable(): void
	{
		$table = $this->getTableSchema();
		if ($table->ENGINE !== $this->engine) {
			$this->connection->query("ALTER TABLE %table ENGINE " . $this->engine, $this->name);
		}
		if ($table->CHARACTER_SET_NAME !== $this->charset) {
			$this->connection->query("ALTER TABLE %table DEFAULT CHARSET " . $this->charset, $this->name);
		}
		if ($table->COLLATION_NAME !== $this->collate) {
			$this->connection->query("ALTER TABLE %table COLLATE " . $this->collate, $this->name);
		}
	}

	/**
	 * Zmeni nazvy sloupcu tabulky
	 * @throws QueryException
	 */
	private function changeColumns(): void
	{
		$change = [];
		foreach ($this->oldColumns as $name => $column) {
			if ($this->columnExists($name)) {
				$change[] = "[$name] $column";
			}
		}
		if (!empty($change)) {
			$this->connection->query("ALTER TABLE %table CHANGE " . implode(', CHANGE ', $change), $this->name);
		}
	}

	/**
	 * Upravi klice a sloupce tabulky
	 * @throws QueryException
	 */
	private function modifyColumnsAndKeys(): void
	{
		$dropKeys = $dropColumns = $modify = $add = $primKey = [];

		// sloupce
		$columns = $this->columns;
		foreach ($this->connection->query('SHOW FULL COLUMNS FROM %table', $this->name) as $column) {
			$name = $column->Field;

			if (isset($columns[$name])) {
				if (!$columns[$name]->equals($column)) {
					$modify[] = "$columns[$name]";
				}
				unset($columns[$name]);
			} else {
				$dropColumns[] = "[$name]";
			}
		}
		if (!empty($columns)) {
			$add[] = '(' . implode(",\n", $columns) . ')';
		}

		// primarni klic
		foreach ($this->connection->query('SHOW INDEX FROM %table WHERE Key_name = %s', $this->name, 'PRIMARY') as $index) {
			$primKey[] = $index->Column_name;
		}

		if (!$this->primaryKey->equals($primKey)) {
			if (!empty($primKey)) {
				$dropKeys[] = 'PRIMARY KEY';
			}
			if (!empty($this->primaryKey)) {
				$add[] = $this->primaryKey;
			}
		}

		// klice
		$keys = $this->keys;
		foreach ($this->getKeys() as $key) {
			$name = $key->name;

			if (isset($keys[$name])) {
				if ($keys[$name]->equals($key)) {
					unset($keys[$name]);
					continue;
				}
			}
			$dropKeys[] = "INDEX [$name]";
		}
		if (!empty($keys)) {
			$add = array_merge($add, $keys);
		}

		// foreign key
		$constraints = $this->constraints;
		foreach ($this->getConstraits() as $constrait) {
			$name = $constrait->CONSTRAINT_NAME;
			if (isset($constraints[$name])) {
				if ($constraints[$name]->equals($constrait)) {
					unset($constraints[$name]);
					continue;
				}
			}
			$dropKeys[] = "FOREIGN KEY [$name]";
		}
		if (!empty($constraints)) {
			$add = array_merge($add, $constraints);
		}

		// drop key
		if (!empty($dropKeys)) {
			$this->connection->query("ALTER TABLE %table DROP " . implode(', DROP ', $dropKeys), $this->name);
		}

		// modify
		if (!empty($modify)) {
			$this->connection->query("ALTER TABLE %table MODIFY " . implode(', MODIFY ', $modify), $this->name);
		}

		// add
		if (!empty($add)) {
			$this->connection->query('SET foreign_key_checks = 1');
			$this->connection->query("ALTER TABLE %table ADD " . implode(', ADD ', $add), $this->name);
			$this->connection->query('SET foreign_key_checks = 0');
		}

		// check relation tables
		foreach ($this->relationTables as $table) {
			$table->check();
		}

		// migration
		foreach ($this->migration as $func) {
			$result = $this->connection->query('SELECT * FROM %table', $this->name);
			foreach ($result as $row) {
				$func($row, $this->connection);
			}
		}

		// drop columns
		if (!empty($dropColumns)) {
			$this->connection->query("ALTER TABLE %table DROP " . implode(', DROP ', $dropColumns), $this->name);
		}
	}

	/**
	 * @param string $value
	 * @return string
	 */
	public function escapeString(string $value): string
	{
		$this->connection->reconnect();
		return $this->connection->getDriver()->convertStringToSql((string) $value);
	}

	/**
	 * Vrati primarni klic
	 * @return PrimaryKey|null
	 */
	protected function getPrimaryKey(): ?PrimaryKey
	{
		return $this->primaryKey;
	}

	/**
	 * Pridavek za dotaz (partition atd)
	 * @param string $addition
	 */
	public function add(string $addition): void
	{
		$this->addition = $addition;
	}

	/**
	 * Prida sloupec
	 * @param string $name
	 * @return Column
	 */
	public function addColumn(string $name): Column
	{
		return $this->columns[$name] = new Column($this, $name);
	}

	/**
	 * Prida primarni klic
	 * @param string $name
	 * @return Column
	 */
	public function addPrimaryKey(string $name): Column
	{
		$column = $this->addColumn($name);
		$this->setPrimaryKey($name);
		return $column;
	}

	/**
	 * Nastavi cizi klic
	 * @param string $name
	 * @param string|Table $mapperClass klic uz musi byt v tabulce nastaven
	 * @param mixed $onDelete false => RESTRICT, true => CASCADE, null => SET null
	 * @param mixed $onUpdate false => RESTRICT, true => CASCADE, null => SET null
	 * @return Column
	 */
	public function addForeignKey(string $name, $mapperClass, $onDelete = true, $onUpdate = false): Column
	{
		$table = $this->getTableData($mapperClass);

		$constrait = new Constrait($name, $this, $table, $onDelete, $onUpdate);

		$this->constraints[$constrait->name] = $constrait;
		return $constrait->column;
	}

	/**
	 * Odebere sloupec
	 * @param string $name
	 */
	public function removeColumn(string $name): void
	{
		unset($this->columns[$name]);
	}

	/**
	 * Nastavi fulltext
	 * @param string ...$key
	 * @return self
	 */
	public function addFulltext(string ...$name): self
	{
		$key = new Index(...$name);
		$key->setFulltext();
		$this->keys[$key->name] = $key;
		return $this;
	}

	/**
	 * Nastavi hodnotu sloupce na unikatni
	 * @param  string ...$key
	 * @return self
	 */
	public function addUnique(string ...$name): self
	{
		$key = new Index(...$name);
		$key->setUnique();
		$this->keys[$key->name] = $key;
		return $this;
	}

	/**
	 * Nastavi klic
	 * @param string[] ...$name
	 * @return self
	 */
	public function addKey(string ...$name): self
	{
		$key = new Index(...$name);
		$this->keys[$key->name] = $key;
		return $this;
	}

	/**
	 * Nastavi primarni klic
	 * @param string[] ...$key
	 * @return self
	 */
	public function setPrimaryKey(string ...$key): self
	{
		$this->primaryKey = new PrimaryKey($this, ...$key);
		return $this;
	}

	/**
	 * Nastavi auto increment
	 * @param int $first
	 * @return self
	 */
	public function setAutoIncrement(int $first): self
	{
		$this->autoIncrement = $first;
		return $this;
	}

	/**
	 * Vrati nazev tabulky a jeji klic
	 * @param string|Table $table
	 * @return self
	 * @throws InvalidArgumentException
	 */
	private function getTableData($table): self
	{
		if ($table instanceof Table) {
			return $table;
		} elseif (is_subclass_of($table, Mapper::class)) {
			/* @var $mapper Mapper */
			$mapper = $this->container->getByType($table);
			return $mapper->getStructure();
		} else {
			throw new InvalidArgumentException;
		}
	}

	/**
	 * Vrati schema tabulky
	 * @return Row|null
	 * @throws QueryException
	 */
	private function getTableSchema(): ?Row
	{
		return $this->connection->query("
			SELECT
				[tab.ENGINE],
				[col.COLLATION_NAME],
				[col.CHARACTER_SET_NAME]
			FROM [information_schema.TABLES] tab
			JOIN [information_schema.COLLATION_CHARACTER_SET_APPLICABILITY] col ON [tab.TABLE_COLLATION] = [col.COLLATION_NAME]
			WHERE [tab.TABLE_SCHEMA] = %s
				AND [tab.TABLE_NAME] = %s",
			$this->database,
			$this->name)->fetch();
	}

	/**
	 * Vrati schema cizich klicu
	 * @return Result|null
	 * @throws QueryException
	 */
	private function getConstraits(): ?Result
	{
		return $this->connection->query("
			SELECT
				[col.CONSTRAINT_NAME],
				[col.COLUMN_NAME],
				[col.REFERENCED_TABLE_NAME],
				[col.REFERENCED_COLUMN_NAME],
				[ref.UPDATE_RULE],
				[ref.DELETE_RULE]
			FROM [information_schema.REFERENTIAL_CONSTRAINTS] ref
			JOIN [information_schema.KEY_COLUMN_USAGE] col ON [ref.CONSTRAINT_NAME] = [col.CONSTRAINT_NAME] AND [ref.CONSTRAINT_SCHEMA] = [col.CONSTRAINT_SCHEMA]
			WHERE [ref.UNIQUE_CONSTRAINT_SCHEMA] = %s
				AND [ref.TABLE_NAME] = %s",
			$this->database,
			$this->name);
	}

	/**
	 * Vrati schema klicu
	 * @return Key[]
	 * @throws QueryException
	 */
	private function getKeys(): array
	{
		/* @var $result Key[] */
		$result = [];
		$rows = $this->connection->query("
			SELECT
				[INDEX_NAME],
				[COLUMN_NAME],
				[INDEX_TYPE],
				[NON_UNIQUE],
				[SEQ_IN_INDEX]
			FROM [information_schema.STATISTICS]
			WHERE [TABLE_SCHEMA] = %s
				AND [TABLE_NAME] = %s
				AND [INDEX_NAME] != %s",
			$this->database,
			$this->name,
			'PRIMARY');
		foreach ($rows as $row) {
			$name = $row->INDEX_NAME;
			if (isset($result[$name])) {
				$obj = $result[$name];
			} else {
				$obj = new Key;
			}
			$obj->name = $name;
			$obj->addColumn($row->SEQ_IN_INDEX, $row->COLUMN_NAME);
			$obj->type = $row->INDEX_TYPE;
			$obj->unique = !$row->NON_UNIQUE;

			$result[$name] = $obj;
		}
		return $result;
	}

	private function columnExists(string $name): bool
	{
		$row = $this->connection->query("
			SELECT COUNT([COLUMN_NAME]) num
				FROM [information_schema.COLUMNS]
				WHERE [TABLE_SCHEMA] = %s
				AND [TABLE_NAME] = %s
				AND [COLUMN_NAME] = %s",
			$this->database,
			$this->name,
			$name)->fetch();
		return $row->num > 0 ? true : false;
	}

	public function serialize(): string
	{
		$unserialized = [
			'name' => $this->name,
			'engine' => $this->engine,
			'charset' => $this->charset,
			'collate' => $this->collate,
			'columns' => serialize($this->columns),
			'primaryKey' => serialize($this->primaryKey),
			'keys' => serialize($this->keys),
			'constraints' => serialize($this->constraints),
			'autoIncrement' => $this->autoIncrement,
			'addition' => $this->addition,
			'relationTables' => serialize($this->relationTables),
			'prefix' => $this->prefix,
			'defaultDataFile' => $this->defaultDataFile
		];
		return serialize($unserialized);
	}

	public function unserialize($serialized): void
	{
		$unserialized = unserialize($serialized);

		$this->name = $unserialized['name'];
		$this->engine = $unserialized['engine'];
		$this->charset = $unserialized['charset'];
		$this->collate = $unserialized['collate'];
		$this->columns = unserialize($unserialized['columns']);
		$this->primaryKey = unserialize($unserialized['primaryKey']);
		$this->keys = unserialize($unserialized['keys']);
		$this->constraints = unserialize($unserialized['constraints']);
		$this->autoIncrement = $unserialized['autoIncrement'];
		$this->addition = $unserialized['addition'];
		$this->relationTables = unserialize($unserialized['relationTables']);
		$this->prefix = $unserialized['prefix'];
		$this->defaultDataFile = $unserialized['defaultDataFile'];
	}
}

interface ITableFactory
{
	public function create(string $name, string $prefix): Table;
}


<?php
namespace yimaTheme\Manager;

use yimaTheme\Manager;
use yimaTheme\Theme\Locator;
use yimaTheme\Theme\LocatorDefaultInterface;
use yimaTheme\Theme\LocatorInterface;
use yimaTheme\Theme\Theme;
use Zend\EventManager\SharedEventManagerInterface;
use Zend\EventManager\SharedListenerAggregateInterface;
use Zend\Http\PhpEnvironment\Response;
use Zend\Mvc\MvcEvent;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use Zend\Stdlib\ArrayUtils;
use Zend\View\Model\ViewModel;
use Zend\View\Resolver as ViewResolver;

/**
 * Class DefaultListenerAggregate
 *
 * @package yimaTheme\Manager
 */
class DefaultListenerAggregate extends Manager implements
    SharedListenerAggregateInterface,
    ServiceManagerAwareInterface
{
    /**
     * @var ServiceManager
     */
    protected $sm;

    /**
     * @var Locator
     */
    protected $themeLocator;

    /**
     * @var array Initialized Themes
     */
    protected $attainedThemes = array();
    /**
     * @var array Attained Themes PathStack
     */
    protected $pathStacks = array();

    /**
     * @var Manager
     */
    protected $manager;

    /**
     * Attach one or more listeners
     *
     * Implementors may add an optional $priority argument; the SharedEventManager
     * implementation will pass this to the aggregate.
     *
     * @param SharedEventManagerInterface $events
     */
    public function attachShared(SharedEventManagerInterface $events)
    {
        $events->attach('Zend\Mvc\Application', MvcEvent::EVENT_BOOTSTRAP, array($this, 'onMvcBootstrapLast'), -100000);

        $events->attach('Zend\Mvc\Application', MvcEvent::EVENT_RENDER, array($this, 'onRenderAddPathStacks'), -900);
        $events->attach('Zend\Mvc\Application', MvcEvent::EVENT_RENDER, array($this, 'onRenderSpecLayout'), -1000);
    }

    // --- Events Methods ---------------------------------------------------------------------------------------------------------------------

    /**
     * MVC Event Listener
     *
     * - get Theme From Locator
     * - initialize theme if not
     * - set theme template to default viewModel template name
     *   if not exists
     * - set Resolved Theme to Event as ViewModel
     *
     * @param MvcEvent $e
     */
    public function onMvcBootstrapLast(MvcEvent $e)
    {
        $this->checkMVC(); // test application startup config to match our need

        /** @var $themeLocator Locator */
        $themeLocator = clone $this->getThemeLocator(); // we have to detach strategies
        // Attain to Base ViewModel to Children Themes Append To ... {
        $themAsViewModel = false;
        while($theme = $themeLocator->getPreparedThemeObject())
        {
            // store attained themes list
            $this->attainedThemes[] = $theme;

            $this->pathStacks[] = $theme->getThemesPath().DIRECTORY_SEPARATOR. $theme->getName();

            // initialize theme bootstrap, also we can know theme final after initialize
            if (!$theme->isInitialized())
                $theme->init();

            if ($theme->isFinalTheme()) {
                $themAsViewModel = spl_object_hash($theme); // use to add children themes to final
                $defTemplate = $e->getViewModel()->getTemplate();
                if (!$theme->getTemplate())
                    $theme->setTemplate($defTemplate); // set default template name

                // set themeObject as ViewModel
                $e->setViewModel($theme);
                break;
            }

            // attain to next template
            $lastStrategy = $themeLocator->getResolverObject()
                ->getLastStrategyFound();
            $themeLocator->getResolverObject()
                ->dettach($lastStrategy); // remove last detector
        }

        /** @var Theme $t */
        foreach($this->attainedThemes as $t) {
            if ($themAsViewModel && spl_object_hash($t) === $themAsViewModel)
                continue; // This is a Final Theme Child will added to

            if ($t->getTemplate())
                // if child theme has a template to render
                $e->getViewModel()->addChild($t, null, true);
        }
        // ... }

        // TODO sometimes we need to render a widget in action, and with current template viewScript file
        $viewTemplatePathStack = $this->sm->get('ViewTemplatePathStack');
        foreach (array_reverse($this->pathStacks) as $path) { // child top and final theme must list last
            $viewTemplatePathStack->addPath($path);
        }
    }

    /**
     * MVC Event Listener
     *
     * @param MvcEvent $e
     */
    public function onRenderAddPathStacks(MvcEvent $e)
    {
        $r = $e->getResult();
        if (!$r instanceof ViewModel)
            return; // we don't get Renderer Result
        // allow viewScripts to append with others on content variable
        else $r->setAppend(true);

        // add path stacks
        $viewTemplatePathStack = $this->sm->get('ViewTemplatePathStack');
        foreach (array_reverse($this->pathStacks) as $path) { // child top and final theme must list last
            $viewTemplatePathStack->addPath($path);
        }
    }

    /**
     * Change layout
     *
     * @param MvcEvent $e
     */
    public function onRenderSpecLayout(MvcEvent $e)
    {
        $r = $e->getResult();
        if (! $r instanceof ViewModel || $r->terminate())
            return;

        $model = $e->getViewModel();

        $themeLocator  = $this->getThemeLocator();

        // we want theme path stack registered before
        #$this->onRenderAddPathStacks($e);

        // get Layout from Locator
        $mvcLayout = $themeLocator->getMvcLayout($e);
        if ($mvcLayout) {
            // we want same on theme layout name if not set
            $model->setTemplate($mvcLayout);
        }
    }

    /**
     * Check Current MVC View Resolver To Match With Class Strategy
     *
     * @return bool
     * @throws \Exception
     */
    protected function checkMVC()
    {
        // check ViewResolver service to match our need. {
        $viewResolver   = $this->sm->get('ViewResolver');

        $return = true;
        if ($viewResolver instanceof ViewResolver\AggregateResolver) {
            if ($viewResolver->count() == 2) {
                $defResolvers = array('Zend\View\Resolver\TemplateMapResolver','Zend\View\Resolver\TemplatePathStack');
                foreach($viewResolver->getIterator()->toArray() as $i=>$ro) {
                    if ($defResolvers[$i] != get_class($ro)) {
                        $return = false;
                        break;
                    }
                }
            } else {
                $return = false;
            }
        } else {
            $return = false;
        }

        $viewTemplatePathStack   = $this->sm->get('ViewTemplatePathStack');
        if (! $viewTemplatePathStack instanceof ViewResolver\TemplatePathStack) {
            throw new \Exception('yimaTheme work with PathStack');
        }

        return $return;
    }

    // -------------------------------------------------------------------------------------------------------------------------------------------

    /**
     * Get ThemeLocator
     *
     * @throws \Exception
     * @return LocatorDefaultInterface
     */
    public function getThemeLocator()
    {
        if (! $this->themeLocator) {
            // use default theme locator to resolve theme object
            /** @var $defaultThemeLocator \yimaTheme\Theme\Locator */
            $defaultThemeLocator = $this->sm->get('yimaTheme.ThemeLocator');
            if (!$defaultThemeLocator instanceof LocatorDefaultInterface) {
                throw new \Exception(
                    'Default Theme Locator Service (yimaTheme.ThemeLocator) must instance of yimaTheme\Theme\LocatorDefaultInterface'
                );
            }
            $this->themeLocator = $defaultThemeLocator;
        }

        return $this->themeLocator;
    }

    /**
     * Set ThemeLocator
     *
     * @param LocatorDefaultInterface $themeLocator
     *
     * @return $this
     */
    public function setThemeLocator(LocatorDefaultInterface $themeLocator)
    {
        $this->sm->setInvokableClass('yimaTheme.ThemeLocator', $themeLocator);

        $this->themeLocator = $themeLocator;

        return $this;
    }

    // --- implemented methods ------------------------------------------------------------------------------------------------------------------

    /**
     * Detach all previously attached listeners
     *
     * @param SharedEventManagerInterface $events
     */
    public function detachShared(SharedEventManagerInterface $events)
    {
        // TODO: Implement detachShared() method.
    }

    /**
     * Set service manager
     *
     * @param ServiceManager $serviceManager
     */
    public function setServiceManager(ServiceManager $serviceManager)
    {
        $this->sm = $serviceManager;
    }
}


<?php

namespace Zf2Forum\Model\Message;

use ZfcBase\Mapper\AbstractDbMapper;
use Zf2Forum\Module as Zf2Forum;
use Zf2Forum\Service\DbAdapterAwareInterface;
use Zend\Stdlib\Hydrator\HydratorInterface;

class MessageMapper extends AbstractDbMapper implements MessageMapperInterface, DbAdapterAwareInterface
{
    protected $tableName = 'forum_reply';
    protected $messageIDField = 'id';
    protected $threadIDField = 'forum_topic_id';

    /**
     * getMessageById
     *
     * @param int $messageId
     * @return MessageInterface
     */
    public function getMessageById($messageId)
    {
        $select = $this->getSelect()
                       ->where(array($this->messageIDField => $messageId));
        return $this->select($select)->current();
    }

    /**
     * getMessagesByThread
     *
     * @param int $threadId
     * @param int $limit
     * @param int $offest
     * @return array of Zf2Forum\Model\Message\MessageInterface's
     */
    public function getMessagesByTopic($threadId, $limit = 25, $offset = 0)
    {
        $select = $this->getSelect()
                       ->where(array($this->threadIDField => $threadId));
        return $this->select($select);
    }

    /**
     * persist - persists a message to the database.
     *
     * @param MessageInterface $message
     * @return MessageInterface
     */
    public function persist(MessageInterface $message)
    {
        if ($message->getMessageId() > 0) {
            $this->update($message, null, null, new MessageHydrator);
        } else {
            $this->insert($message, null, new MessageHydrator);
        }

        return $message;
    }

    /**
     * insert - Inserts a new message into the database, using the specified hydrator.
     *
     * @param MessageInterface $entity
     * @param String $tableName
     * @param HydratorInterface $hydrator
     * @return unknown
     */
    protected function insert($entity, $tableName = null, HydratorInterface $hydrator = null)
    {
        $result = parent::insert($entity, $tableName, $hydrator);
        $entity->setMessageId($result->getGeneratedValue());
        return $result;
    }

    /**
     * update - Updates an existing message in the database.
     * @param MessageInterface $entity
     * @param String $where
     * @param String $tableName
     * @param HydratorInterface $hydrator
     */
    protected function update($entity, $where = null, $tableName = null, HydratorInterface $hydrator = null)
    {
        if (!$where) {
            $where = 'id = ' . $entity->getId();
        }
        return parent::update($entity, $where, $tableName, $hydrator);
    }
}


<?php

namespace Gbuckingham89\EloquentUuid\Traits;

use Illuminate\Support\Str;

/**
 * Class UuidForKey
 * @package Gbuckingham89\EloquentUuid\Traits
 */
trait UuidForKey
{

    /**
     * Stop the model thinking it has an incrementing ID
     *
     * @return bool
     */
    public function getIncrementing() : bool
    {
        return false;
    }

    /**
     * Hook into the 'creating' method to set the key to be a UUID
     *
     * @return void
     */
    public static function bootUuidForKey()
    {
        static::creating(function ($model) {
            $model->incrementing = false;
            $model->attributes[$model->getKeyName()] = (string) Str::orderedUuid();
        });
    }

}


<?php
/**
 * Logger utilizing an ironbound\db table
 *
 * @author    Iron Bound Designs
 * @since     1.0
 * @license   MIT
 * @copyright Iron Bound Designs, 2015.
 */

namespace IronBound\DBLogger;

use IronBound\DB\Query\Simple_Query;
use Psr\Log\AbstractLogger;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel;

/**
 * Class Logger
 * @package IronBound\DBLogger
 */
class Logger extends AbstractLogger {

	/**
	 * @var Simple_Query
	 */
	private $query;

	/**
	 * @var AbstractTable
	 */
	private $table;

	/**
	 * Logger constructor.
	 *
	 * @param AbstractTable $table
	 * @param Simple_Query  $query
	 */
	public function __construct( AbstractTable $table, Simple_Query $query ) {
		$this->table = $table;
		$this->query = $query;
	}

	/**
	 * Logs with an arbitrary level.
	 *
	 * The current user ID will be automatically logged.
	 * To bypass this pass `false` for `_user` in `$context`.
	 *
	 * To assign the log to a group, pass the group name ( up to 20 chars ) for `_group` in `$context`.
	 *
	 * Any additional columns in the db can be passed in `$context` by prefixing the column name with `_`.
	 * For example, passing a key `_product_id` in `$context` will save whatever value given in the column
	 * named `product_id`. If the column does not exist the value will be silently discarded.
	 *
	 * The he context array will be recorded as JSON, sans the `exception` if passed and an Exception object.
	 *
	 * @param mixed  $level
	 * @param string $message
	 * @param array  $context
	 *
	 * @return null
	 */
	public function log( $level, $message, array $context = array() ) {

		if ( ! in_array( $level, array(
			LogLevel::ALERT,
			LogLevel::CRITICAL,
			LogLevel::DEBUG,
			LogLevel::EMERGENCY,
			LogLevel::ERROR,
			LogLevel::INFO,
			LogLevel::NOTICE,
			LogLevel::WARNING
		) )
		) {
			throw new InvalidArgumentException( sprintf( "Invalid log level '%s'", $level ) );
		}

		$message      = (string) $message;
		$interpolated = $this->interpolate( $message, $context );

		if ( isset( $context['exception'] ) && $context['exception'] instanceof \Exception ) {

			/** @var \Exception $exception */
			$exception = $context['exception'];

			$class = get_class( $context['exception'] );
			$trace = $exception->getTraceAsString();

			unset( $context['exception'] );
		} else {
			$class = '';
			$trace = '';
		}

		$data = array(
			'level'     => $level,
			'message'   => $interpolated,
			'lgroup'    => isset( $context['_group'] ) ? substr( $context['_group'], 0, 20 ) : '',
			'time'      => date( 'Y-m-d H:i:s' ),
			'ip'        => $this->get_ip(),
			'exception' => $class,
			'trace'     => $trace,
			'context'   => @wp_json_encode( $context )
		);

		$valid_columns = $this->table->get_columns();

		foreach ( $context as $key => $value ) {

			if ( strpos( $key, '_' ) === 0 ) {

				$context_column_name = substr( $key, 1 );

				if ( isset( $valid_columns[ $context_column_name ] ) ) {
					$data[ $context_column_name ] = $value;
				}
			}
		}

		if ( ! isset( $data['user'] ) && is_user_logged_in() ) {
			$data['user'] = get_current_user_id();
		}

		$this->query->insert( $data );
	}

	/**
	 * Interpolates context values into the message placeholders.
	 *
	 * @param string $message
	 * @param array  $context
	 *
	 * @return string
	 */
	protected function interpolate( $message, array $context = array() ) {

		// build a replacement array with braces around the context keys
		$replace = array();

		foreach ( $context as $key => $val ) {
			$replace[ '{' . $key . '}' ] = $this->convert_value_to_string( $val );
		}

		// interpolate replacement values into the message and return
		return strtr( $message, $replace );
	}

	/**
	 * Converts a value of unknown type to a string.
	 *
	 * @param mixed $value
	 *
	 * @return string
	 */
	protected function convert_value_to_string( $value ) {

		if ( $this->is_resource( $value ) ) {

			$type = get_resource_type( $value );

			return "(Resource:$type)";
		}

		if ( is_object( $value ) ) {

			if ( $value instanceof \Exception || $value instanceof \Throwable ) {
				return '(' . get_class( $value ) . "#{$value->getCode()}:{$value->getMessage()})";
			} elseif ( $value instanceof \DateTime || ( interface_exists( '\DateTimeInterface' ) && $value instanceof \DateTimeInterface ) ) {
				return $value->format( \DateTime::ATOM );
			} elseif ( method_exists( $value, '__toString' ) ) {
				return (string) $value;
			} else {

				$class = get_class( $value );

				return "($class)";
			}
		}

		if ( is_array( $value ) ) {
			return '(Array)';
		}

		if ( is_scalar( $value ) ) {
			return $value;
		}

		if ( $value === null ) {
			return '(Null)';
		}

		return '(Invalid)';
	}

	/**
	 * Check if a value is a resource.
	 *
	 * @since 1.0
	 *
	 * @param $maybe_resource
	 *
	 * @return bool
	 */
	protected function is_resource( $maybe_resource ) {
		return null !== @get_resource_type( $maybe_resource );
	}

	/**
	 * Get the current IP address.
	 *
	 * @link  http://stackoverflow.com/a/19189952
	 *
	 * @since 1.0
	 *
	 * @return string
	 */
	protected function get_ip() {

		return getenv( 'HTTP_CLIENT_IP' ) ?:
			getenv( 'HTTP_X_FORWARDED_FOR' ) ?:
				getenv( 'HTTP_X_FORWARDED' ) ?:
					getenv( 'HTTP_FORWARDED_FOR' ) ?:
						getenv( 'HTTP_FORWARDED' ) ?:
							getenv( 'REMOTE_ADDR' ) ?: false;
	}

	/**
	 * Purge all records older than 'n' days.
	 *
	 * @since 1.0
	 *
	 * @param int   $days_older_than
	 * @param \wpdb $wpdb
	 */
	public function purge( $days_older_than = 60, \wpdb $wpdb ) {

		$days_older_than = absint( $days_older_than );

		$tn = $this->table->get_table_name( $wpdb );

		$sql = "DELETE FROM {$tn} WHERE time < DATE_SUB(NOW(), INTERVAL $days_older_than DAY)";

		$wpdb->query( $sql );
	}
}


<?php

namespace Monospice\SpicyIdentifiers\Tools;

use Monospice\SpicyIdentifiers\Tools\CaseFormat;
use Monospice\SpicyIdentifiers\Tools\Interfaces;

/**
 * Parses an identifer string into an array of parts
 *
 * @author Cy Rossignol <cy.rossignol@yahoo.com>
 */
class Parser implements Interfaces\Parser
{
    // Inherit Doc from Interfaces\Parser
    public static function parse($identifier, $format)
    {
        $parseMethod = 'parseFrom' . $format;

        if (method_exists(get_class(), $parseMethod)) {
            return static::$parseMethod($identifier);
        }

        throw new \InvalidArgumentException($format . ' is an unsupported ' .
            'parse format. If applicable, "UPPERCASE_*" and "*_WITH_ACRONYM" ' .
            'formats should be parsed as the corresponding generic format');
    }

    /**
     * Parses each part of an identifier parts array using the given format
     * and merges the new parts into the array
     *
     * @param array $parts The array of identifier parts to parse
     * @param string $format The string constant representing the format to
     * parse from
     *
     * @return array The resulting array of parsed identifier parts
     */
    protected static function parseEachPart(array $parts, $format)
    {
        $lastPartKey = count($parts) - 1;

        for ($p = 0; $p <= $lastPartKey; $p++) {
            $parsedPart = static::parse($parts[$p], $format);
            $numNewParts = count($parsedPart);

            if ($numNewParts > 1) {
                array_splice($parts, $p, 1, $parsedPart);
                $p += $numNewParts;
                $lastPartKey += $numNewParts - 1;
            }
        } // end of parts for loop

        return $parts;
    }

    // Inherit Doc from Interfaces\Parser
    public static function parseFromMixedCase($identifier, array $formats)
    {
        $partsArray = [$identifier];
        $parseCamelCaseLast = false;

        foreach ($formats as $format) {
            if ($format === CaseFormat::CAMEL_CASE) {
                $parseCamelCaseLast = true;
                continue;
            }

            $partsArray = static::parseEachPart($partsArray, $format);
        }

        if ($parseCamelCaseLast) {
            $partsArray = static::parseEachPart(
                $partsArray,
                CaseFormat::CAMEL_CASE
            );
        }

        return $partsArray;
    }

    // Inherit Doc from Interfaces\Parser
    public static function parseFromCamelCase($identifier)
    {
        $camelCasePattern = '/' .
            // Do not attempt to split along a capital letter at the
            // beginning of the string:
            '(?!^)' .
            // Split along any of the following:
            '(' .
                // A sequence that starts with a capital letter following a
                // lowercase letter or number:
                '(?<=[a-z0-9])(?=[A-Z])' .
                // Or
                '|' .
                // A sequence that starts with a capital letter followed by a
                // lowercase letter or number:
                '(?=[A-Z][a-z])' .
            ')' .
        '/';

        return preg_split($camelCasePattern, $identifier);
    }

    // Inherit Doc from Interfaces\Parser
    public static function parseFromCamelCaseExtended(
        $identifier,
        $upper = '\xc0-\xd6\xd8-\xdf',
        $lower = '\x7f-\xbf\xd7\xe0-\xff'
    ) {

        $camelCasePattern = '/' .
            // Do not attempt to split along a capital letter at the
            // beginning of the string:
            '(?!^)' .
            // Split along any of the following:
            '(' .
                // A sequence that starts with a capital letter following a
                // lowercase letter or number:
                '(?<=[a-z0-9' . $lower . '])(?=[A-Z' . $upper . '])' .
                // Or
                '|' .
                // A sequence that starts with a capital letter followed by a
                // lowercase letter or number:
                '(?=[A-Z' . $upper . '][a-z' . $lower . '])' .
            ')' .
        // Accept multibyte characters in the identifier
        '/u';

        return preg_split($camelCasePattern, $identifier);
    }

    // Inherit Doc from Interfaces\Parser
    public static function parseFromUnderscore($identifier)
    {
        return explode('_', $identifier);
    }

    // Inherit Doc from Interfaces\Parser
    public static function parseFromHyphen($identifier)
    {
        return explode('-', $identifier);
    }
}


<?php

namespace Unite\UnisysApi\Exceptions;

use Exception;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use League\OAuth2\Server\Exception\OAuthServerException;
use Spatie\Permission\Exceptions\PermissionDoesNotExist;

class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that are not reported.
     *
     * @var array
     */
    protected $dontReport = [
        OAuthServerException::class,
        AuthenticationException::class,
        ModelNotFoundException::class,
    ];

    /**
     * A list of the inputs that are never flashed for validation exceptions.
     *
     * @var array
     */
    protected $dontFlash = [
        'password',
        'password_confirmation',
    ];

    /**
     * Report or log an exception.
     *
     * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
     *
     * @param  \Exception  $exception
     * @return void
     */
    public function report(Exception $exception)
    {
        if (app()->bound('sentry') && $this->shouldReport($exception) && app()->environment() == 'production') {
            app('sentry')->captureException($exception);
        }

        parent::report($exception);
    }

    /**
     * Prepare exception for rendering.
     *
     * @param  \Exception  $e
     * @return \Exception
     */
    protected function prepareException(Exception $e)
    {
        $e = parent::prepareException($e);

        if($e instanceof PermissionDoesNotExist || $e instanceof AuthorizationException) {
            $e = new AuthenticationException($e->getMessage());
        }

        return $e;
    }

    /**
     * Convert an authentication exception into a response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Illuminate\Auth\AuthenticationException  $exception
     * @return \Illuminate\Http\Response
     */
    protected function unauthenticated($request, AuthenticationException $exception)
    {
        return response()->json(['message' => $exception->getMessage()], 401);
    }
}


<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the MIT license.
 */

namespace ZfrAwsUtils\DynamoDb\Pagination;

use Assert\Assertion;
use Aws\DynamoDb\DynamoDbClient;
use Aws\DynamoDb\Marshaler;
use Aws\Result;
use ZfrAwsUtils\Exception\InvalidArgumentException;

/**
 * @author Daniel Gimenes
 */
class DynamoDbPaginator
{
    const ORDER_ASC          = 'ASC';
    const ORDER_DESC         = 'DESC';
    const DIRECTION_PREVIOUS = 'previous';
    const DIRECTION_NEXT     = 'next';

    /**
     * @var DynamoDbClient
     */
    private $dynamoDbClient;

    /**
     * @var Marshaler
     */
    private $marshaler;

    /**
     * @param DynamoDbClient $dynamoDbClient
     * @param Marshaler      $marshaler
     */
    public function __construct(DynamoDbClient $dynamoDbClient, Marshaler $marshaler)
    {
        $this->dynamoDbClient = $dynamoDbClient;
        $this->marshaler      = $marshaler;
    }

    /**
     * @param array                     $query
     * @param int                       $limit
     * @param string                    $order
     * @param CursorStrategyInterface   $cursorStrategy
     * @param string|null               $cursor
     * @param string|null               $direction
     *
     * @return PaginationResult
     */
    public function paginate(
        array $query,
        int $limit,
        string $order,
        CursorStrategyInterface $cursorStrategy,
        string $cursor = null,
        string $direction = null
    ): PaginationResult {
        Assertion::greaterOrEqualThan($limit, 1, 'Limit must be greater or equal than 1');
        Assertion::choice($order, [self::ORDER_ASC, self::ORDER_DESC], 'Order must be either ASC or DESC');

        // Setup ordering, true for ASC and false for DESC
        $query['ScanIndexForward'] = self::ORDER_ASC === $order;

        // If no cursor is present, then get the first page
        if (null === $cursor) {
            $result = $this->runQuery($query, $limit);

            return new PaginationResult(
                $this->unmarshalResources($result),
                null,
                $this->buildCursorFromLastItem($cursorStrategy, $result)
            );
        }

        // Otherwise, set exclusive start key from cursor
        $query['ExclusiveStartKey'] = $this->marshaler->marshalItem($cursorStrategy->buildKeyFromCursor($cursor));

        if (self::DIRECTION_PREVIOUS === $direction) {
            // Reverse query ordering when paginating to previous page
            $query['ScanIndexForward'] = ! $query['ScanIndexForward'];

            $result = $this->runQuery($query, $limit);

            return new PaginationResult(
                array_reverse($this->unmarshalResources($result)),
                $this->buildCursorFromLastItem($cursorStrategy, $result),
                $this->buildCursorFromFirstItem($cursorStrategy, $result)
            );
        }

        if (self::DIRECTION_NEXT === $direction) {
            $result = $this->runQuery($query, $limit);

            return new PaginationResult(
                $this->unmarshalResources($result),
                $this->buildCursorFromFirstItem($cursorStrategy, $result),
                $this->buildCursorFromLastItem($cursorStrategy, $result)
            );
        }

        throw new InvalidArgumentException('Invalid pagination direction, it must be either previous or next');
    }

    /**
     * Runs the DynamoDB query with limit + 1
     * and removes the last item from result if item count exceeds page limit
     *
     * @param array $query
     * @param int   $limit
     *
     * @return Result
     */
    private function runQuery(array $query, int $limit): Result
    {
        $query['Limit']  = $limit + 1;
        $result          = $this->dynamoDbClient->query($query);
        $result['Items'] = array_slice($result['Items'], 0, $limit);

        return $result;
    }

    /**
     * @param Result $result
     * @return array
     */
    private function unmarshalResources(Result $result): array
    {
        return array_map(function (array $item) {
            return $this->marshaler->unmarshalItem($item);
        }, $result->get('Items'));
    }

    /**
     * @param CursorStrategyInterface $cursorStrategy
     * @param Result                  $result
     *
     * @return null|string
     */
    private function buildCursorFromFirstItem(CursorStrategyInterface $cursorStrategy, Result $result)
    {
        if (0 === $result->get('Count')) {
            return null;
        }

        $items     = $result->get('Items');
        $firstItem = $this->marshaler->unmarshalItem(reset($items));

        return $cursorStrategy->buildCursorFromKey($firstItem);
    }

    /**
     * @param CursorStrategyInterface $cursorStrategy
     * @param Result                  $result
     *
     * @return null|string
     */
    private function buildCursorFromLastItem(CursorStrategyInterface $cursorStrategy, Result $result)
    {
        if (! $result->hasKey('LastEvaluatedKey')) {
            return null;
        }

        $items    = $result->get('Items');
        $lastItem = $this->marshaler->unmarshalItem(end($items));

        return $cursorStrategy->buildCursorFromKey($lastItem);
    }
}


<?php

namespace ThemeManager;

class ThemeManager
{
    /**
     * @var \ThemeManager\ThemeCollection
     */
    private $themes;

    /**
     * @param ThemeCollection $themes
     */
    public function __construct(ThemeCollection $themes)
    {
        $this->themes = $themes;
    }

    /**
     * @return array
     */
    public function getAllThemeNames()
    {
        return $this->themes()->allThemeNames();
    }

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

    /**
     * @return array
     */
    public function getInvalidThemesCount()
    {
        return $this->themes()->invalidCount();
    }

    /**
     * @param $name
     *
     * @return bool|Theme
     */
    public function getTheme($name)
    {
        return $this->themes()->getTheme($name);
    }

    /**
     * @param $name
     *
     * @return bool
     */
    public function themeExists($name)
    {
        return $this->themes()->themeExists($name);
    }

    /**
     * @return \ThemeManager\ThemeCollection
     */
    public function themes()
    {
        return $this->themes;
    }

    /**
     * @return \ThemeManager\ThemeCollection
     */
    public function all()
    {
        return $this->themes();
    }

    /**
     * @return \ThemeManager\Theme
     */
    public function first()
    {
        return $this->all()->first();
    }

    /**
     * @return \ThemeManager\Theme
     */
    public function last()
    {
        return $this->all()->last();
    }

    /**
     * @return int
     */
    public function countAll()
    {
        return $this->all()->count();
    }

    /**
     * @param         $path
     *
     * @return $this
     */
    public function addThemeLocation($path)
    {
        if (! empty($path) && ! $this->themes()->pathExists($path)) {
            $addLocation = (new Starter(true))->start($path, $this->themes()->getRequiredFields(), $this->themes()->getExceptionOnInvalid());

            $all = array_merge($this->getInvalidThemes(), $addLocation->all(), $addLocation->getInvalidThemes());

            $this->themes = $this->themes()->merge($all, $path);
        }

        return $this;
    }

    /**
     * @return $this
     */
    public function returnThis()
    {
        return $this;
    }
}


<?php

namespace hypeJunction\Prototyper\Elements;

abstract class Field implements FieldProperties, FieldInput, FieldOutput, FieldData, FieldStickyValues, FieldValidation {

	/**
	 * Short name of the field (used as metadata or annotation or relationship name)
	 * @var string
	 */
	protected $shortname;

	/**
	 * Type of the input (used for rendering input views and validation)
	 * @var string
	 */
	protected $type;

	/**
	 * Type of entity
	 * @var string
	 */
	protected $entity_type;

	/**
	 * Subtype of entity
	 * @var string
	 */
	protected $entity_subtype;

	/**
	 * Class name
	 * @var string
	 */
	protected $class_name;

	/**
	 * Type of the model used to store and retrieve values
	 * @var string
	 */
	protected $data_type;

	/**
	 * Type of value (used for validation)
	 * @var type
	 */
	protected $value_type;

	/**
	 * Elgg view to display an input (if different from "input/$type"
	 * @var string
	 */
	protected $input_view;

	/**
	 * Elgg view to display an output (if different from "output/$type"
	 * @var string
	 */
	protected $output_view;

	/**
	 * Input label
	 * @var boolean|string
	 */
	protected $label;

	/**
	 * Help text
	 * @var boolean|string
	 */
	protected $help;

	/**
	 * Display an access input
	 * @var boolean
	 */
	protected $show_access;

	/**
	 * Allow cloning of input fields
	 * @var boolean
	 */
	protected $multiple;

	/**
	 * Admin only field
	 * @var boolean
	 */
	protected $admin_only;

	/**
	 * Hide on profile
	 * @var boolean
	 */
	protected $hide_on_profile;

	/**
	 * Value passed to the input
	 * @var mixed
	 */
	protected $value;

	/**
	 * Sticky value inherited from failed validation
	 * @var mixed
	 */
	protected $sticky_value;

	/**
	 * Vars passed to the input view
	 * @var \stdClass
	 */
	protected $input_vars;

	/**
	 * Vars passed to the output view
	 * @var \stdClass
	 */
	protected $output_vars;

	/**
	 * Validation status
	 * @var \stdClass
	 */
	protected $validation;

	/**
	 * Validation rules
	 * @var array
	 */
	protected $validation_rules = array();

	/**
	 * Order of the field
	 * @var int
	 */
	protected $priority = 500;

	/**
	 * Flags used for filtering
	 * @var array
	 */
	protected $flags = array();

	/**
	 * Construct a new field
	 *
	 * @param array $options Options
	 */
	public function __construct(array $options = array()) {
		$this->input_vars = new \stdClass();
		$this->output_vars = new \stdClass();
		foreach ($options as $key => $value) {
			$this->set($key, $value);
		}
	}

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

	/**
	 * {@inheritdoc}
	 */
	public function set($name, $value = null) {

		$props = get_object_vars($this);

		switch ($name) {

			default :
				if (array_key_exists($name, $props)) {
					$this->$name = $value;
				} else {
					$this->input_vars->$name = $value;
				}
				break;

			case 'type' :
				$this->setType($value);
				break;

			case 'value_type' :
				$this->setValueType($value);
				break;

			case 'validation' :
			case 'validation_rules' :
				$value = (array) $value;
				foreach ($value as $rule => $expectation) {
					$this->addValidationRule($rule, $expectation);
				}
				break;

			case 'flags' :
				if (is_string($value)) {
					$value = string_to_tag_array($value);
				}
				$this->flags = $value;
				break;

			case 'data-icon-sizes':
				// added for BC
				$this->input_vars->icon_sizes = $value;
				break;
		}
	}

	/**
	 * {@inheritdoc}
	 */
	public function getShortname() {
		return $this->shortname;
	}

	/**
	 * {@inheritdoc}
	 */
	public function setType($type = '') {
		$this->type = $type;
	}

	/**
	 * {@inheritdoc}
	 */
	public function getType() {
		return ($this->type) ? $this->type : 'text';
	}

	/**
	 * {@inheritdoc}
	 */
	public function setValueType($value_type = '') {
		$this->value_type = $value_type;
		if (!$this->getValidationRule('type')) {
			$this->addValidationRule('type', $value_type);
		}
		return $this;
	}

	/**
	 * {@inheritdoc}
	 */
	public function getValueType() {
		return $this->value_type;
	}

	/**
	 * {@inheritdoc}
	 */
	public function getDefaultValue() {
		return $this->value;
	}

	/**
	 * {@inheritdoc}
	 */
	public function viewInput($vars = array()) {
		$vars['field'] = $this;
		$data_type = $this->getDataType();
		return elgg_view("prototyper/input/$data_type", $vars);
	}

	/**
	 * {@inheritdoc}
	 */
	public function getInputView() {
		$view = (isset($this->input_view)) ? $this->input_view : "input/$this->type";
		return $view;
	}

	/**
	 * {@inheritdoc}
	 */
	public function viewOutput($vars = array()) {
		$vars['field'] = $this;
		$data_type = $this->getDataType();
		return elgg_view("prototyper/output/$data_type", $vars);
	}

	/**
	 * {@inheritdoc}
	 */
	public function getOutputView() {
		return (isset($this->output_view)) ? $this->output_view : "output/$this->type";
	}

	/**
	 * {@inheritdoc}
	 */
	public function hasAccessInput() {
		return false;
	}

	/**
	 * {@inheritdoc}
	 */
	public function getFlags() {
		return (is_array($this->flags)) ? $this->flags : array();
	}

	/**
	 * {@inheritdoc}
	 */
	public function isRequired() {
		return ($this->input_vars->required);
	}

	/**
	 * {@inheritdoc}
	 */
	public function isAdminOnly() {
		return ($this->admin_only);
	}

	/**
	 * {@inheritdoc}
	 */
	public function isHiddenOnProfile() {
		return ($this->hide_on_profile);
	}
	/**
	 * {@inheritdoc}
	 */
	public function isMultiple() {
		return ($this->multiple);
	}

	/**
	 * {@inheritdoc}
	 */
	public function getInputVars(\ElggEntity $entity) {
		$this->input_vars->entity = ($entity->guid) ? $entity : null;
		$this->input_vars->required = $this->isRequired();

		if (!empty($this->input_vars->options_values) && is_array($this->input_vars->options_values)) {
			$lang = get_language();
			$options_values = array();

			foreach ($this->input_vars->options_values as $o_key => $o_value) {
				if (is_array($o_value)) {
					$o_value = elgg_extract($lang, $o_value, elgg_echo(implode(':', array_filter(array(
						'option',
						$this->entity_type,
						$this->entity_subtype,
						$this->getShortname(),
						$o_key,
					)))));
				}
				$options_values[$o_key] = $o_value;
			}

			if ($this->type == 'checkboxes' || $this->type == 'radio') {
				$this->input_vars->options = array_flip($options_values);
			}
			$this->input_vars->options_values = $options_values;
		}

		$vars = (array) $this->input_vars;

		$clean = array('ui_sections', 'relationship', 'inverse_relationship', 'bilateral');
		foreach ($clean as $key) {
			unset($vars[$key]);
		}

		return elgg_trigger_plugin_hook('input_vars', 'prototyper', array(
			'field' => $this,
			'entity' => $entity,
				), $vars);
	}

	/**
	 * {@inheritdoc}
	 */
	public function getOutputVars(\ElggEntity $entity) {
		$this->output_vars->entity = $entity;
		return (array) $this->output_vars;
	}

	/**
	 * {@inheritdoc}
	 */
	public function getLabel($lang = '', $raw = false) {

		$key = implode(':', array_filter(array(
			'label',
			$this->entity_type,
			$this->entity_subtype,
			$this->getShortname()
		)));

		if ($raw) {
			return $key;
		}

		if ($this->label === false) {
			return false;
		}

		if (!$lang) {
			$lang = get_language();
		}

		if (is_string($this->label)) {
			$translation = $this->label;
		} else if (is_array($this->label)) {
			$translation = elgg_extract($lang, $this->label);
		}

		return ($translation) ? $translation : elgg_echo($key, array(), $lang);
	}

	/**
	 * {@inheritdoc}
	 */
	public function getHelp($lang = '', $raw = false) {

		$key = implode(':', array_filter(array(
			'help',
			$this->entity_type,
			$this->entity_subtype,
			$this->getShortname()
		)));

		if ($raw) {
			return $key;
		}

		if ($this->help === false) {
			return false;
		}

		if (!$lang) {
			$lang = get_language();
		}

		if (is_string($this->help)) {
			$translation = $this->help;
		} else if (is_array($this->help)) {
			$translation = elgg_extract($lang, $this->help);
		}

		return ($translation) ? $translation : elgg_echo($key, array(), $lang);
	}

	/**
	 * {@inheritdoc}
	 */
	public function addValidationRule($rule, $expectation) {
		if ($rule && $expectation) {
			$this->validation_rules[$rule] = $expectation;
		}
		return $this;
	}

	/**
	 * {@inheritdoc}
	 */
	public function getValidationRule($rule) {
		if (isset($this->validation_rules[$rule])) {
			return $this->validation_rules[$rule];
		}
		return false;
	}

	/**
	 * {@inheritdoc}
	 */
	public function getValidationRules() {
		return $this->validation_rules;
	}

	/**
	 * {@inheritdoc}
	 */
	public function applyValidationRules($value = '', ValidationStatus $validation = null, \ElggEntity $entity = null) {

		if (!$validation instanceof ValidationStatus) {
			$validation = new ValidationStatus;
		}

		$validation_rules = $this->getValidationRules();
		if (!empty($validation_rules)) {
			foreach ($validation_rules as $rule => $expectation) {
				$validation = elgg_trigger_plugin_hook("validate:$rule", 'prototyper', array(
					'rule' => $rule,
					'field' => $this,
					'value' => $value,
					'expectation' => $expectation,
					'entity' => $entity,
						), $validation);

				if (!$validation instanceof ValidationStatus) {
					elgg_log("'validate:$rule,'prototyper' hook must return an instance of ValidationStatus", 'ERROR');
					$validation = new ValidationStatus();
				}
			}
		}
		return $validation;
	}

	/**
	 * {@inheritdoc}
	 */
	public function setValidation($status = true, $messages = array()) {
		$this->validation = new ValidationStatus($status, $messages);
		return $this;
	}

	/**
	 * {@inheritdoc}
	 */
	public function getValidation() {
		return ($this->validation instanceof ValidationStatus) ? $this->validation : new ValidationStatus();
	}

	/**
	 * {@inheritdoc}
	 */
	public function isValid() {
		return $this->getValidation()->getStatus();
	}

	/**
	 * {@inheritdoc}
	 */
	public function getValidationMessages() {
		return $this->getValidation()->getMessages();
	}

	/**
	 * {@inheritdoc}
	 */
	public function setStickyValue($value = '') {
		$this->sticky_value = $value;
		return $this;
	}

	/**
	 * {@inheritdoc}
	 */
	public function getStickyValue() {
		return $this->sticky_value;
	}

}


<?php

namespace Unite\UnisysApi\QueryBuilder;

use Closure;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Unite\UnisysApi\Models\Model;
use Unite\UnisysApi\QueryBuilder\Parsers\FilterParser;
use Unite\UnisysApi\QueryBuilder\Parsers\LimitParser;
use Unite\UnisysApi\QueryBuilder\Parsers\OrderParser;
use Unite\UnisysApi\QueryBuilder\Parsers\PageParser;
use Unite\UnisysApi\QueryBuilder\Parsers\SearchParser;
use Unite\UnisysApi\QueryBuilder\Types\Column;
use Unite\UnisysApi\QueryBuilder\Types\Filter;
use Unite\UnisysApi\QueryBuilder\Types\Join;
use Unite\UnisysApi\QueryBuilder\Types\OrderBy;
use Unite\UnisysApi\QueryBuilder\Types\Search;

class QueryBuilder
{
    /** @var \Illuminate\Database\Eloquent\Builder */
    protected $builder;

    /** @var \Illuminate\Http\Request */
    protected $request;

    /** @var integer */
    protected $limit;

    /** @var integer */
    protected $page;

    /** @var OrderBy */
    protected $orderBy;

    /** @var Search|null */
    protected $search;

    /** @var Collection|Filter[] */
    protected $filters;

    /** @var Collection|Join[] */
    protected $joins;

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

    /** @var Model */
    public $baseModel;

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

    /** @var \Unite\UnisysApi\Http\Resources\Resource */
    public $resourceClass;

    /** @var JoinResolver */
    public $joinResolver;

    /** @var array */
    public $virtualFields;

    public function __construct(Builder $builder, ? Request $request = null)
    {
        $this->builder = new Builder($builder->getQuery());

        $this->initializeFromBuilder($builder);

        $this->request = $request ?? request();
    }

    public function getBuilder()
    {
        return $this->builder;
    }

    protected static function availableRequestKeys()
    {
        return collect([
            'limit', 'order', 'page', 'search', 'filter'
        ]);
    }

    protected function data()
    {
        $data = [];

        $this->availableRequestKeys()->each(function ($key) use (&$data) {
            $data[$key] = $this->request->query($key);
        });

        return $data;
    }

    protected function parseRequest()
    {
        $requestData = $this->data();

        $this->limit = (new LimitParser($this))->parse($requestData['limit']);
        $this->orderBy = (new OrderParser($this))->parse($requestData['order']);
        $this->page = (new PageParser($this))->parse($requestData['page']);
        $this->search = (new SearchParser($this))->parse($requestData['search']);
        $this->filters = (new FilterParser($this))->parse($requestData['filter']);
    }

    public function computeColumn(string $column)
    {
        $this->resolveColumn($column);

        return $this;
    }

    public function resolveColumn(string $column)
    {
        $column = new Column($column, $this->baseTable, $this->resourceClass::tableTrough()->toArray());

        if($column->needJoin) {
            $this->joinResolver->addColumn($column);
        }

        return $column;
    }

    public function resolveJoins()
    {
        $this->joinResolver->makeJoins();

        return $this->joinResolver->getJoins();
    }

    /**
     * Add the model, scopes, eager loaded relationships, local macro's and onDelete callback
     * from the $builder to this query builder.
     *
     * @param \Illuminate\Database\Eloquent\Builder $builder
     */
    protected function initializeFromBuilder(Builder $builder)
    {
        $this->builder->setModel($builder->getModel())
            ->setEagerLoads($builder->getEagerLoads());

        $this->baseTable = $this->builder->getModel()->getTable();
        $this->baseModel = $this->builder->getModel();
        $this->modelClass = get_class($this->builder->getModel());

        $this->joinResolver = new JoinResolver($this);
    }

    protected function setResourceClass(string $value)
    {
        $this->resourceClass = $value;

        return $this;
    }

    protected function init()
    {
        $this->loadAllVirtualFields();
    }

    protected function loadAllVirtualFields()
    {
        $this->virtualFields = $this->resourceClass::getVirtualFields();
    }

    /**
     * @param \Unite\UnisysApi\Http\Resources\Resource $resourceClass
     */
    protected function getTableFromModelClass(string $resourceClass)
    {
        return with(new $resourceClass)->getTable();
    }

    /**
     * @param \Unite\UnisysApi\Http\Resources\Resource $resourceClass
     */
    protected function getTableFromResourceClass(string $resourceClass)
    {
        /** @var Model $modelClass */
        $modelClass = $resourceClass::modelClass();

        return $this->getTableFromModelClass($modelClass);
    }

    /**
     * @param \Unite\UnisysApi\Http\Resources\Resource $resourceClass
     * @param Request $request
     */
    public static function for(string $resourceClass, ? Request $request = null) : self
    {
        /** @var \Illuminate\Database\Eloquent\Builder $baseQuery */
        $baseQuery = ($resourceClass::modelClass())::query();
        $baseQuery->with($resourceClass::getEagerLoads());

        $builder = new static($baseQuery, $request ?? request());
        $builder->setResourceClass($resourceClass);
        $builder->init();
        $builder->parseRequest();

        return $builder;
    }

    /**
     * @return \Illuminate\Database\Eloquent\Collection|static[]
     */
    public function get()
    {
        $this->buildQuery();

        return $this->builder->get($this->baseSelect());
    }

    /**
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    public function paginate()
    {
        $this->buildQuery();

        return $this->builder->paginate($this->limit, $this->baseSelect(), config('query-filter.page_name'), $this->page);
    }

    protected function baseSelect()
    : array
    {
        return [ $this->baseTable . '.*' ];
    }

    public function buildQuery()
    {
        $this->joins = $this->resolveJoins();

        $this->addSearchToBuilder();

        $this->addFilterToBuilder();

        $this->addOrderByToBuilder();

        $this->addJoinsToBuilder();

        $this->builder->select($this->baseSelect());

        $this->builder->distinct();

        return $this->builder;
    }

    protected function addSearchToBuilder()
    {
        if($this->search->query) {
            $this->builder->where(function (Builder $query) {
                $this->search->columns->each(function(Column $column) use ($query) {
                    if($this->isVirtualField($column->fullColumn)) {
                          $this->executeVirtualField($query, $column->fullColumn, $this->search->query);
                    } else {
                        $query->orWhere($column->fullColumn, 'like', ($this->search->fulltext ? '%' : '') . $this->search->query . '%');
                    }
                });
            });
        }
    }

    protected function executeVirtualField(Builder $query, $field, $value)
    {
        return $this->virtualFields[$field]($query, $value);
    }

    protected function isVirtualField(string $field)
    {
        return isset($this->virtualFields[$field]);
    }

    protected function addFilterToBuilder()
    {
        foreach ($this->filters as $filter) {
            $this->builder->where(function ($query) use ($filter) {
                /** @var $query Builder */
                $this->addFilterDataToBuilder($query, $filter);
            });
        }
    }

    protected function addFilterDataToBuilder(Builder $query, Filter $filter)
    {
        if($filter->operator === 'and') {
            $this->builder->groupBy($this->baseTable . '.id');
            $this->builder->havingRaw('COUNT(*) = ?', [$filter->data->count()]);
        }

        foreach ($filter->data as $dataItem) {
            if ($filter->operator === 'or' || $filter->operator === 'and') {
                if($this->isVirtualField($filter->column->fullColumn)) {
                    $this->executeVirtualField($query, $filter->column->fullColumn, $dataItem->value);
                } else {
                    $query->orWhere($filter->column->fullColumn, $dataItem->operator, $dataItem->value);
                }
            } elseif ($filter->operator === 'between') {
                $query->whereBetween($filter->column->fullColumn, $filter->getDataValues());
            }
        }

        return $query;
    }

    protected function addOrderByToBuilder()
    {
        if($this->isVirtualField($this->orderBy->column->fullColumn)) {
            $this->executeVirtualField($this->builder, $this->orderBy->column->fullColumn, $this->orderBy->direction);
        } else {
            $this->builder->orderBy($this->orderBy->column->fullColumn, $this->orderBy->direction);
        }
    }

    protected function addJoinsToBuilder()
    {
        $this->joins->each(function (Join $join) {
            if($join->conditions->isEmpty()) {
                $this->builder = $this->builder->join($join->table, $join->first, '=', $join->second);
            } else {
                $this->builder = $this->builder->join($join->table, function ($q) use ($join) {
                    /** @var \Illuminate\Database\Query\JoinClause $q */
                    $q->on($join->first, '=', $join->second);

                    foreach ($join->conditions as $condition) {
                        $q->where($condition['column'], '=', $condition['value']);
                    }
                });
            }
        });
    }
}


<?php

namespace Unite\UnisysApi\Commands;

use Illuminate\Console\Command;
use Illuminate\Routing\Router;
use Spatie\Permission\Exceptions\PermissionDoesNotExist;
use Spatie\Permission\Models\Permission;
use Illuminate\Support\Str;
use Illuminate\Routing\Route;

class PermissionsSync extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'unisys-api:sync-permissions';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Check route names and update permissions in database';

    /**
     * The router instance.
     *
     * @var \Illuminate\Routing\Router
     */
    protected $router;

    /**
     * An array of all the registered routes.
     *
     * @var \Illuminate\Routing\RouteCollection
     */
    protected $routes;

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct(Router $router)
    {
        parent::__construct();

        $this->router = $router;
        $this->routes = $router->getRoutes();
    }

    /*
     * Execute the console command.
     */
    public function handle()
    {
        if (count($this->routes) == 0) {
            return $this->error("Application doesn't have any routes.");
        }

        $routes = $this->getRoutes();
        $global = \Unite\UnisysApi\Models\Permission::getGlobalPermissions();

        $permissions = $routes->merge($global);

        Permission::all()->each(function (Permission $permission) use($permissions) {
            if(!$permissions->contains($permission->name)) {
                $permission->delete();
            }
        });

        $permissions->each(function ($name) {
            try {
                Permission::findByName($name);
            } catch (PermissionDoesNotExist $e) {
                Permission::create(['name' => $name]);
            }
        });

        $this->info('Permissions was synced');
    }

    /**
     * Compile the routes into a displayable format.
     *
     * @return \Illuminate\Support\Collection
     */
    protected function getRoutes()
    {
        return collect($this->routes)
            ->map(function ($route) {
                return $this->getRouteInformation($route);
            })
            ->reject(function ($item) {
                return is_null($item);
            })
            ->sortBy('name')
            ->pluck('name');
    }

    /**
     * Get the route information for a given route.
     *
     * @param  \Illuminate\Routing\Route  $route
     * @return array
     */
    protected function getRouteInformation(Route $route)
    {
        return $this->filterRoute([
            'name'   => $route->getName(),
            'isAuthorized' => $this->isAuthorized($route),
        ]);
    }

    /**
     * Get before filters.
     *
     * @param  \Illuminate\Routing\Route  $route
     * @return string
     */
    protected function isAuthorized($route)
    {
        if(collect($route->gatherMiddleware())->contains('authorize')) {
            return true;
        }

        return false;
    }

    /**
     * Filter the route by not empty name.
     *
     * @param  array  $route
     * @return array|null
     */
    protected function filterRoute(array $route)
    {
        if(!$route['isAuthorized']) {
            return null;
        }

        if (Str::contains($route['name'], '')) {
            return null;
        }

        return $route;
    }
}


<?php
namespace WMS\Library\BusinessRulesEngine;

use Symfony\Component\Config\Resource\ResourceInterface;

/**
 * Collection holding multiple Rule instances
 *
 * @author Andrew Moore <me@andrewmoore.ca>
 */
class RuleCollection implements \IteratorAggregate, \Countable
{
    /**
     * @var ResourceInterface[]
     */
    private $resources = array();

    /**
     * @var Rule[]
     */
    private $rules = array();

    public function __clone()
    {
        foreach ($this->rules as $name => $rule) {
            $this->rules[$name] = clone $rule;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->rules);
    }

    /**
     * {@inheritdoc}
     */
    public function count()
    {
        return count($this->rules);
    }

    /**
     * Adds a rule.
     *
     * @param string $name The rule name
     * @param Rule   $rule A Rule instance
     */
    public function add($name, Rule $rule)
    {
        unset($this->rules[$name]);

        $this->rules[$name] = $rule;
    }

    /**
     * Returns all rules in this collection.
     *
     * @return Rule[] An array of rules
     */
    public function all()
    {
        return $this->rules;
    }

    /**
     * Gets a rule by name.
     *
     * @param string $name The rule name
     *
     * @return Rule|null A Rule instance or null when not found
     */
    public function get($name)
    {
        return isset($this->rules[$name]) ? $this->rules[$name] : null;
    }

    /**
     * Removes a rule or an array of rules by name from the collection
     *
     * @param string|array $name The rule name or an array of rule names
     */
    public function remove($name)
    {
        foreach ((array)$name as $n) {
            unset($this->rules[$n]);
        }
    }

    /**
     * Adds a tag for all rules.
     *
     * @param string $name       The tag name
     * @param array  $attributes An array of attributes
     */
    public function addTag($name, array $attributes = array())
    {
        foreach ($this->rules as $rule) {
            $rule->addTag($name, $attributes);
        }
    }

    /**
     * Clears all tags for a given name in all Rules of this collection.
     *
     * @param string $name The tag name
     *
     * @return Rule
     */
    public function clearTag($name)
    {
        foreach ($this->rules as $rule) {
            $rule->clearTag($name);
        }
    }

    /**
     * Clears the tags for all rules.
     *
     * @return Rule The current instance
     *
     * @api
     */
    public function clearTags()
    {
        foreach ($this->rules as $rule) {
            $rule->clearTags();
        }
    }

    /**
     * Adds a rule collection at the end of the current set by appending all
     * rules of the added collection.
     *
     * @param RuleCollection $collection A RuleCollection instance
     */
    public function addCollection(RuleCollection $collection)
    {
        // we need to remove all rules with the same names first because just replacing them
        // would not place the new rule at the end of the merged array
        foreach ($collection->all() as $name => $rule) {
            unset($this->rules[$name]);
            $this->rules[$name] = $rule;
        }

        $this->resources = array_merge($this->resources, $collection->getResources());
    }

    public function findTaggedRules($tagName)
    {
        $taggedCollection = new RuleCollection();

        foreach ($this->rules as $name => $rule) {
            if ($rule->hasTag($tagName)) {
                $taggedCollection->add($name, clone $rule);
            }
        }

        return $taggedCollection;
    }

    /**
     * Returns an array of resources loaded to build this collection.
     *
     * @return ResourceInterface[] An array of resources
     */
    public function getResources()
    {
        return array_unique($this->resources);
    }

    /**
     * Adds a resource for this collection.
     *
     * @param ResourceInterface $resource A resource instance
     */
    public function addResource(ResourceInterface $resource)
    {
        $this->resources[] = $resource;
    }
}


<?php
namespace Nkey\Caribu\Mvc\Util;

use Nkey\Caribu\Mvc\Controller\Request;

/**
 * Provides the request parsing functionality
 *
 * @author Maik Greubel <greubel@nkey.de>
 *
 *         This file is part of Caribu MVC package
 */
trait RequestParser
{

    /**
     * Parse the context prefix variables to determine in which path
     * context the request has been performed.
     *
     * @param Request $request
     */
    private static function parseContextPrefix(Request &$request, $serverVars = array())
    {
        // Since apache 2.3.13 we have now an additional index which provides the context
        if (isset($serverVars['CONTEXT_PREFIX']) && $serverVars['CONTEXT_PREFIX'] != '') {
            $request->setContextPrefix( $serverVars['CONTEXT_PREFIX'] . '/' );
        } elseif (isset($serverVars['REDIRECT_BASE'])) {
            // Try to determine the context from redirect base
            $request->setContextPrefix ( $serverVars['REDIRECT_BASE'] );
        } elseif (isset($serverVars['SCRIPT_FILENAME']) && isset($serverVars['SCRIPT_NAME'])) {
            // Fallback - get context out of script path
            if (isset($serverVars['HTTP_HOST'])) {
                $scriptName = preg_replace('/^.+[\\\\\\/]/', '', $serverVars['SCRIPT_FILENAME']);
                $request->contextPrefix = str_replace($scriptName, '', $serverVars['SCRIPT_NAME']);
            }
        }
    }

    /**
     * Parse the prepared uri into its parts
     *
     * @param Request $request
     *            The unprepared request object
     * @param string $uri
     *            The prepared uri
     * @param string $defaultController
     *            The name of default controller if nothing is requested
     * @param string $defaultAction
     *            The name of default action if nothing is requested
     *
     * @return array Parsed parts for later usage
     */
    private static function parseUri(Request &$request,
    		$uri, $defaultController, $defaultAction)
    {
        // All beyond the context prefix is our application request uri
        $contextUri = $uri;
        if (null != $request->getContextPrefix() && '/' != $request->getContextPrefix()) {
        	$contextUri = str_replace($request->getContextPrefix(), '', $uri);
        }

        // Split parts
        $parts = array();
        if ($contextUri != '') {
            while (isset($contextUri[0]) && $contextUri[0] == '/') {
                $contextUri = substr($contextUri, 1);
            }
            $parts = explode('/', $contextUri);
        }

        // Check if there was a controller requested
        if (count($parts) > 0) {
            $request->setController( ucfirst(trim($parts[0])) );
            array_shift($parts);
            if (! $request->getController()) {
                $request->setController( $defaultController );
            }
        }

        // Check if there was an action requested
        if (count($parts) > 0) {
            $request->setAction( trim($parts[0]) );
            array_shift($parts);
            if (! $request->getAction()) {
                $request->setAction( $defaultAction );
            }
        }

        return $parts;
    }

    /**
     * Parse a single http header element into parameter for the request object
     *
     * @param Request $req
     *            The destination request object
     * @param array $serverVars
     *            The server variables provided by sapi
     * @param string $elementName
     *            The element to parse
     * @param string $paramName
     *            The destination parameter name
     */
    private static function parseElement(Request &$req,
    		$serverVars, $elementName, $paramName)
    {
        if (isset($serverVars[$elementName])) {
        	$req->setParam( $paramName, $serverVars[$elementName] );
        }
    }

    /**
     * Parse the server variables which represents HTTP headers into parameter values for the request object
     *
     * @param Request $req
     *            The request object
     * @param array $serverVars
     *            The server variables provided by sapi
     */
    private static function parseParameters(Request &$req, $serverVars)
    {
        self::parseElement($req, $serverVars, 'HTTP_ACCEPT', 'Accept');
        self::parseElement($req, $serverVars, 'HTTP_ACCEPT_LANGUAGE', 'Accept-Language');
        self::parseElement($req, $serverVars, 'HTTP_ACCEPT_ENCODING', 'Accept-Encoding');
        self::parseElement($req, $serverVars, 'HTTP_UA_CPU', 'User-Agent-CPU');
        self::parseElement($req, $serverVars, 'HTTP_USER_AGENT', 'User-Agent');
        self::parseElement($req, $serverVars, 'HTTP_HOST', 'Host');
        self::parseElement($req, $serverVars, 'HTTP_CACHE_COTROL', 'Cache-Control');
        self::parseElement($req, $serverVars, 'HTTP_CONNECTION', 'Connection');
        self::parseElement($req, $serverVars, 'HTTP_X_FORWARDED_FOR', 'X-Forwarded-For');

        if (isset($req->params['Accept-Language'])) {
            $accepted = explode(',', $req->params['Accept-Language']);
            $req->params['Accept-Language-Best'] = $accepted[0];
            foreach ($accepted as $acceptedLang) {
                $matches = array();
                // TODO: Respect the quality field from rfc2616
                if (preg_match("/^((?i)[a-z]{2}[-_](?:[a-z]{2}){1,2}(?:_[a-z]{2})?).*/", $acceptedLang, $matches)) {
                    $req->params['Accept-Language-Best'] = $matches[1];
                    break;
                }
            }
        }
    }

    /**
     * Parse the remote host variables to determine client address
     *
     * @param Request $request
     */
    private static function parseRemoteHost(Request &$request, $serverVars = array())
    {
        if (isset($serverVars['REMOTE_ADDR'])) {
            $request->remoteHost = $serverVars['REMOTE_ADDR'];
        }
        if (isset($serverVars['HTTP_X_FORWARDED_FOR'])) {
            $request->remoteHost = $serverVars['HTTP_X_FORWARDED_FOR'];
        }
    }

    /**
     * Parse the super globals for request parameters
     *
     * @param Request $request
     *            Request object to put the parameters in
     */
    private static function parseGetPostSessionCookie(Request &$request)
    {
        foreach ($_GET as $name => $value) {
            $request->params[$name] = $value;
        }
        foreach ($_POST as $name => $value) {
            $request->params[$name] = $value;
        }
        foreach ($_COOKIE as $name => $value) {
            $request->params[$name] = $value;
        }
        foreach ($_FILES as $name => $value) {
            $request->params[$name] = $value;
        }
        if (isset($_SESSION)) {
            foreach ($_SESSION as $name => $value) {
                $request->params[$name] = $value;
            }
        }
    }
}


<?php

namespace Unite\UnisysApi\Http\Controllers;

use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Unite\UnisysApi\Http\Requests\UploadRequest;
use Unite\UnisysApi\Http\Resources\MediaResource;
use Unite\UnisysApi\Services\UploadService;

/**
 * @property-read \Unite\UnisysApi\Repositories\Repository $repository
 */
trait HandleUploads
{
    /**
     * Upload file
     *
     * @param int $id
     * @param UploadRequest $request
     * @param UploadService $uploadService
     * @return MediaResource
     */
    public function uploadFile(int $id, UploadRequest $request, UploadService $uploadService)
    {
        if(!$object = $this->repository->find($id)) {
            abort(404);
        }

        $file = $request->file('file');

        $media = $uploadService->upload($object, $file);

        \Cache::tags('response')->flush();

        return new MediaResource($media);
    }

    /**
     * Get all Files
     *
     * @param int $id
     * @return AnonymousResourceCollection|MediaResource[]
     */
    public function getFiles(int $id)
    {
        if(!$object = $this->repository->find($id)) {
            abort(404);
        }

        $media = $object->getMedia();

        return MediaResource::collection($media);
    }

    /**
     * Get latest files
     *
     * @param int $id
     * @return AnonymousResourceCollection|MediaResource[]
     */
    public function getLatestFile(int $id)
    {
        if(!$object = $this->repository->find($id)) {
            abort(404);
        }

        $mediaItem = $object->getMedia()->last();

        return MediaResource::collection($mediaItem);
    }
}


<?php

declare(strict_types=1);

namespace Chiron\Container;

use Chiron\Container\Exception\ContainerException;
use Chiron\Container\Exception\EntityNotFoundException;
use Closure;
use InvalidArgumentException;
use Psr\Container\ContainerExceptionInterface;

// TODO : créer une méthode singleton() ou share() => https://github.com/illuminate/container/blob/master/Container.php#L354
// https://github.com/thephpleague/container/blob/master/src/Container.php#L92
//https://github.com/mrferos/di/blob/master/src/Container.php#L99

// TODO : améliorer le Circular exception avec le code : https://github.com/symfony/dependency-injection/blob/master/Container.php#L236

class ContainerAbstract implements ContainerInterface
{
    /**
     * @var bool
     */
    protected $defaultToShared = false;

    /** @var \Chiron\Container\ContainerInterface */
    //public static $instance;

    /** @var \Chiron\Container\Definition[] */
    protected $definitions = [];

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

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

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

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

    protected $resolver;

    /**
     * Array of entries being resolved. Used to avoid circular dependencies and infinite loops.
     *
     * @var array
     */
    protected $entriesBeingResolved = [];

    public function __construct()
    {
        // TODO : créer une méthode getResolver() dans cette classe.
        $this->resolver = new ReflectionResolver($this);
    }

    /**
     * Whether the container should default to defining shared definitions.
     *
     * @param bool $shared
     *
     * @return self
     */
    public function defaultToShared(bool $shared = true): ContainerInterface
    {
        $this->defaultToShared = $shared;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function get($name, bool $new = false)
    {
        return $this->resolve($name, $new);
    }

    /**
     * {@inheritdoc}
     */
    // TODO : mettre en place un systéme de cache dans le cas ou on fait un has() ca va instancier la classe il faudrait la mettre en cache pour éviter de devoir refaire la même chose si on doit faire un get() dans la foulée !!!
    /*
    public function has($name)
    {
        try {
            $this->resolve($name);

            return true;
            // TODO : améliorer le catch et lui attraper toute les exception de type PSR/Container/ContainerException
        } catch (ContainerExceptionInterface $e) {
        }

        return false;
    }*/

    public function has($id)
    {
        if (! isset($this->definitions[$id]) && class_exists($id)) {
            $this->add($id);
        }

        return isset($this->definitions[$id]) ||
                isset($this->services[$id]) ||
                $this->isAlias($id);
    }

    /*
    // TODO : regarder si on peut utiliser cette méthode pour tester le has() !!!!
        public function has($name): bool
        {
            //TODO : on devrait faire une vérif si le paramétre $alias est bien une string sinon on léve une exception !!!!!
            return array_key_exists($name, $this->services) || $this->isAlias($name);
        }
    */

    /**
     * Determine if the given abstract type has been bound.
     *
     * @param string $abstract
     *
     * @return bool
     */
    /*
    //https://github.com/illuminate/container/blob/master/Container.php#L158
    public function bound($abstract)
    {
        return isset($this->bindings[$abstract]) ||
               isset($this->instances[$abstract]) ||
               $this->isAlias($abstract);
    }*/
    /**
     *  {@inheritdoc}
     */
    /*
    public function has($id)
    {
        return $this->bound($id);
    }*/

    /**
     * {@inheritdoc}
     */
    // TODO : à renommer en remove() ????
    // TODO : réfléchir si on conserve cette méthode

    public function destroy(...$names)
    {
        foreach ($names as $name) {
            unset(
                $this->definitions[$name],
                $this->services[$name],
                $this->classes[$name],
                $this->closures[$name]
                // TODO : il faudrait aussi supprimer l'alias !!!!!
            );
        }
    }

    /**
     * {@inheritdoc}
     */
    // TODO : à virer c'est déprecated !!!!
    /*
    public function instance(string $name, $value): DefinitionInterface
    {
        $this->destroy($name);
        $this->instances[$name] = $value;

        return $this->definitions[$name] = new Definition($name, $value);
        //return $this->definitions[] = new Definition($name);
    }*/

    // TODO : renommer en invokable()
    /*
    public function closure(string $name, callable $handler): DefinitionInterface
    {
        return $this->bind($name, $handler);
    }*/

    /**
     * {@inheritdoc}
     */
    // TODO : lui passer un 3 eme paramétre pour savoir si c'est du shared ou non + créer une méthode share() => https://github.com/thephpleague/container/blob/master/src/Container.php#L92
    /*
    public function bind2(string $name, $className = null): DefinitionInterface
    {
        if (! isset($className)) {
            $this->destroy($name);
            $this->classes[$name] = $name;

            return $this->definitions[$name] = new Definition($name, $name);
            //return $this->definitions[] = new Definition($name);
        }
        if (is_string($className) && class_exists($className)) {
            $this->destroy($name, $className);
            $this->classes[$className] = $className;
            $this->alias($name, $className);

            return $this->definitions[$className] = new Definition($className, $className);
            //return $this->definitions[] = new Definition($className);
        } elseif (is_callable($className)) {
            $this->destroy($name);
            $this->closures[$name] = $className;

            return $this->definitions[$name] = new Definition($name, $className);
            //return $this->definitions[] = new Definition($name);
        }

        throw new ContainerException(
            sprintf('Argument 2 must be class name or Closure, "%s" given', is_object($className) ? get_class($className) : gettype($className))
        );
    }*/

    /**
     * Proxy to add with shared as true.
     *
     * @param string $id
     * @param mixed  $concrete
     *
     * @return \League\Container\Definition\DefinitionInterface
     */
    public function share(string $id, $concrete = null): DefinitionInterface
    {
        return $this->add($id, $concrete, true);
    }

    /**
     * Add an item to the container.
     *
     * @param string $id
     * @param mixed  $concrete
     * @param bool   $shared
     *
     * @return \League\Container\Definition\DefinitionInterface
     */
    public function add(string $id, $concrete = null, bool $shared = null): DefinitionInterface
    {
        // handle special case when the $id is the interface name and the $concrete the real class.
        // TODO : bout de code à virer si on recherche directement avec le getAlias du definition
        if (is_string($concrete) && class_exists($concrete)) {
            $this->alias($concrete, $id);
        }

        $concrete = $concrete ?? $id;
        $shared = $shared ?? $this->defaultToShared;

        if (! $concrete instanceof DefinitionInterface) {
            $concrete = new Definition($id, $concrete);
        }

        $this->definitions[$id] = $concrete
            ->setAlias($id)
            ->setShared($shared);

        return $concrete;
    }

    /**
     * {@inheritdoc}
     */
    public function alias(string $alias, string $target): void
    {
        $this->aliases[$alias] = $target;
    }

    /**
     * Determine if a given string is an alias.
     *
     * @param string $name
     *
     * @return bool
     */
    public function isAlias(string $name): bool
    {
        return isset($this->aliases[$name]);
    }

    /**
     * Get the alias for an abstract if available.
     *
     * @param string $abstract
     *
     * @throws \LogicException
     *
     * @return string
     */
    public function getAlias(string $abstract): string
    {
        if (! isset($this->aliases[$abstract])) {
            return $abstract;
        }
        if ($this->aliases[$abstract] === $abstract) {
            throw new ContainerException("[{$abstract}] is aliased to itself.");
        }

        return $this->getAlias($this->aliases[$abstract]);
    }

    /**
     * {@inheritdoc}
     */
    // TODO : méthode à virer
    public function getDefinition(string $name): DefinitionInterface
    {
        $name = $this->getAlias($name);

        if (! array_key_exists($name, $this->definitions)) {
            throw new InvalidArgumentException($name);
        }

        return $this->definitions[$name];
    }

    /**
     * @param string $name
     *
     * @return mixed|object
     */
    protected function resolve($name, bool $new = false)
    {
        // resolve alias
        $name = $this->getAlias($name);

        // TODO : il faudrait aussi vérifier si $new est à false avant de rentrer dans ce test. non ????
        if (array_key_exists($name, $this->services)) {
            return $this->services[$name];
        }
        if (! array_key_exists($name, $this->definitions)) {
            if (! class_exists($name)) {
                throw new EntityNotFoundException("Service '$name' wasn't found in the dependency injection container");
            }
            $this->add($name);
        }

        $definition = $this->definitions[$name];
        //$definition = $this->getDefinition($name);

        $instance = $this->resolver->resolve($definition->getConcrete(), $definition->getAssigns());
        //$instance = $this->resolver->resolve($definition->getConcrete(), $this->convertAssign($definition->assigns));

        /*
        if (array_key_exists($name, $this->classes)) {
            //$instance = $this->resolver->build($this->classes[$name], $this->resolveArguments($definition->assigns));
            $instance = $this->resolver->resolve($this->classes[$name], $definition->assigns);
        } elseif (array_key_exists($name, $this->closures)) {
            //$instance = $this->resolver->call($this->closures[$name], $this->resolveArguments($definition->assigns));
            $instance = $this->resolver->resolve($this->closures[$name], $definition->assigns);
        }*/

        if ($definition->isShared() && $new === false) {
            $this->services[$name] = $instance;
        }

        return $instance;
    }

    // TODO : méthode à virer !!!!
    protected function convertAssign(array $arguments): array
    {
        $argumentsToReturn = [];
        foreach ($arguments as $key => $value) {
            if (is_array($value)) {
                if (array_key_exists('value', $value)) {
                    $argumentsToReturn[$key] = $value['value'];
                }
            } else {
                if ($this->has($value)) {
                    $argumentsToReturn[$key] = $this->get($value);
                }
            }
        }

        return $argumentsToReturn;
    }
}


<?php
namespace yimaTheme\Resolvers;

use Countable;
use IteratorAggregate;
use Zend\Stdlib\PriorityQueue;

class Aggregate implements
    ResolverInterface,
	Countable,
	IteratorAggregate
{
	/**
	 * @var PriorityQueue
	 */
	protected $queue;

	/**
	 * Last Detector found in queue
	 *
	 * @var ResolverInterface
	 */
	protected $lastStrategyFound;

	/**
	 * Constructor
	 * - Instantiate the internal priority queue
	 */
	public function __construct()
	{
		$this->queue = new PriorityQueue();
	}

    /**
	 * Attain To Name based on strategy found in class
	 *
	 * @return string|false
	 */
	public function getName()
	{
		if (0 === count($this->queue)) {
			return false;
		}

        /** @var $detector ResolverInterface */
        foreach ($this->queue as $detector) {
			$name = $detector->getName();
			if (empty($name) && $name !== '0') {
				// No resource found; try next resolver
				continue;
			}

			// Resource found; return it
			$this->lastStrategyFound = $detector;
			return $name;
		}

		return false;
	}

	// Inside Class Usage ..........................................................................................

	/**
	 * Return count of attached resolvers
	 *
	 * @return int
	 */
	public function count()
	{
		return $this->queue->count();
	}

	/**
	 * IteratorAggregate: return internal iterator
	 *
	 * @return PriorityQueue
	 */
	public function getIterator()
	{
		return $this->queue;
	}

	/**
	 * Attach a name resolver strategy
	 *
	 */
	public function attach(ResolverInterface $detector, $priority = 1)
	{
		$this->queue->insert($detector, $priority);
		return $this;
	}

    public function dettach(ResolverInterface $detector)
    {
        $this->queue->remove($detector);

        return $this;
    }

    public function dettachAll()
    {
        foreach($this->queue as $detector) {
            $this->dettach($detector);
        }

        return $this;
    }

	public function getLastStrategyFound()
	{
		return $this->lastStrategyFound;
	}
}