<?php

namespace sndsgd\fs\directory;

use \sndsgd\Fs;

class Hasher
{
    /**
     * Absolute path to the directory to hash
     *
     * @var string
     */
    protected $dir;

    /**
     * A map of relative path => file hash
     *
     * @var array<string,string>
     */
    protected $hashes = [];

    /**
     * @param string $dir The directory to hash
     * @throws \InvalidArgumentException If the directory is not readable
     */
    public function __construct(string $dir)
    {
        $this->dir = Fs::dir($dir);
        if (!$this->dir->test(Fs::EXISTS | Fs::READABLE)) {
            throw new \InvalidArgumentException(sprintf(
                "failed to hash directory; %s",
                $this->dir->getError()
            ));
        }
    }

    /**
     * Get a single hash for all files in the directory
     *
     * @return string
     */
    public function getHash()
    {
        return sha1($this->getHashes());
    }

    /**
     * Get all hashes encoded as pretty printed JSON
     *
     * @return string
     */
    public function getHashes()
    {
        if (empty($this->hashes)) {
            $this->generateHashes();
        }
        return json_encode($this->hashes, \sndsgd\Json::HUMAN);
    }

    /**
     * Create an iterator for looping over files
     * Return type hint excluded for mocking in tests
     */
    protected function createIterator()
    {
        $options = \RecursiveDirectoryIterator::SKIP_DOTS;
        $iterator = new \RecursiveDirectoryIterator($this->dir, $options);
        $options = \RecursiveIteratorIterator::SELF_FIRST;
        return new \RecursiveIteratorIterator($iterator, $options);
    }

    /**
     * Generate hashes for all files found using an iterator
     *
     * @return array<string,string>
     * @throws \RuntimeException If a duplicate directory is encountered
     */
    protected function generateHashes(): array
    {
        $dirLength = strlen($this->dir);
        foreach ($this->createIterator() as $file) {
            if (!$file->isFile()) {
                continue;
            }
            $realpath = $file->getRealPath();
            $path = $file->getPath().DIRECTORY_SEPARATOR.$file->getFilename();

            # skip aliases
            if ($realpath !== $path) {
                continue;
            }

            # remove the relative directory from the file path
            $path = substr($realpath, $dirLength);

            # map hashes by a lowercase version of the path
            # this should prevent issues caused by case sensitive filesystems
            $lowerPath = strtolower($path);
            if (isset($this->hashes[$lowerPath])) {
                $message = "duplicate file encountered: $path ($lowerPath)";
                throw new \RuntimeException($message);
            }

            $this->hashes[$lowerPath] = sha1_file($realpath);
        }

        ksort($this->hashes);
        return $this->hashes;
    }
}


<?php

namespace Miky\Bundle\UserBundle\Entity;
use Miky\Bundle\LocationBundle\Entity\Location;

/**
 * UserPreference
 */
class UserPreference
{
    /**
     * @var int
     */
    private $id;

    /**
     * @var Customer
     */
    protected $customer;

    /**
     * @var Location
     */
    protected $location;

    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set customer
     *
     * @param \Miky\Bundle\UserBundle\Entity\Customer $customer
     *
     * @return UserPreference
     */
    public function setCustomer(\Miky\Bundle\UserBundle\Entity\Customer $customer = null)
    {
        $this->customer = $customer;

        return $this;
    }

    /**
     * Get customer
     *
     * @return \Miky\Bundle\UserBundle\Entity\Customer
     */
    public function getCustomer()
    {
        return $this->customer;
    }

    /**
     * Set location
     *
     * @param \Miky\Bundle\LocationBundle\Entity\Location $location
     *
     * @return UserPreference
     */
    public function setLocation(\Miky\Bundle\LocationBundle\Entity\Location $location = null)
    {
        $this->location = $location;

        return $this;
    }

    /**
     * Get location
     *
     * @return \Miky\Bundle\LocationBundle\Entity\Location
     */
    public function getLocation()
    {
        return $this->location;
    }
}


<?php

/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2018, Abexto - Helicon Software Development / Amylian Project
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * 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 HOLDER 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.
 *
 */

namespace amylian\yii\base\common;

/**
 * Abstract component for encapsulation of Non-Yii classes
 *
 * @author Andreas Prucha, Abexto - Helicon Software Development
 *
 * @property object $inst Wrapped Object Instance ({@see getInst()})
 */
Abstract class BaseInstanceWrapperComponent extends \yii\base\Component implements InstanceWrapperComponentInterface
{

    const EVENT_AFTER_NEW_INST = 'afterNewInst';

    /**
     * @var string Class of the instance to wrap
     */
    public $instClass = null;

    /**
     *  Array of arguments to be passed to the constructor
     *
     * @var array|null Parameters to be passed to constructor
     */
    public $constructorArgs = null;

    /**
     * Additional properties to be set in the wrapped instance
     * @var array Array of propertName => value pairs.
     */
    public $addInstProperties = [];

    /**
     * @var object|null Wrapped object instance
     */
    protected $_inst = null;
    protected $_instInitFinalized = false;

    /**
     *  Array of additional properties to set in the wrapped instance
     *
     * @var array Array of propertyName => Value pairs
     */

    /**
     * Returns an array of component => inst property mappings for automatic configuration of the wraped instance
     *
     * The keys are the property names used in this component
     *
     * The values may be:
     *

     *

     *    true
     *

     *

     *    if the value is true, the {@link setInstProperties()} tries to figure out the best way to use set the value.
     *    If {@link $inst} defines a public property with the same name, it's set.
     *    If {@link $inst} defines a public method with the name setXxxx (where Xxxx is the name of the proprety), it's called.
     *    NOTE: Just the value is passed to the method.
     *    If this has a method named setInstXxxxProperty (where Xxxx is the name of the proprety) this method
     *    is called. The value and the instance of the wrapped object is passed to this method.
     *

     *

     *     Callable:
     *

     *     if the first element of the callable array is null
     *

     *

     *

     *    false
     *

     *

     *      Ignore this property
     *

     *

     *
     * @return array
     */
    protected function getInstPropertyMappings()
    {
        return[];
    }

    /**
     * Sets the value of a property
     *
     * Automatic mapping
     *
     * Automatic mapping is used, if $mappingDefinition is true or contains a property name.
     * In this case, the following steps are performed:
     *
     * 1)   Check if a method named setInstPropertyXxxx exists in $this. If the method exists, it is called and the called
     *      method is responsible to set the property in $inst.
     * 2)   Check if a method name setXxxx exists in $inst. If this method exists, it is called. The property value
     *      is passed in the first parameter.
     * 3)   $inst->Xxxx is set directly.
     *
     * @param type $inst
     * @param type $propertyName
     * @param type $mappingDefinition
     * @param type $propertyValue
     * @return boolean
     * @throws \yii\base\ErrorException
     */
    protected function setInstProperty($inst, $propertyName, $mappingDefinition, $propertyValue = null)
    {
        if ($mappingDefinition === false) {
            return false; // Do not even attempt to set this property
        }
        if (func_num_args() <= 3) {
            $propertyValue = $this->$propertyName;
        }
        if ($mappingDefinition === true ||
                is_string($mappingDefinition)) {
            $instPropertyName = $mappingDefinition === true ? $propertyName : $mappingDefinition;

            $setInstPropertyMethod = 'setInstProperty' . ucfirst($instPropertyName);
            if (method_exists($this, $setInstPropertyMethod)) {
                $this->$setInstPropertyMethod($propertyValue, $inst);
                return;
            } else {
                $instSetterMethod = 'set' . ucfirst($instPropertyName);
                if (method_exists($inst, $instSetterMethod)) {
                    $inst->$instSetterMethod($propertyValue);
                    return;
                } else {
                    $inst->$instPropertyName = $propertyValue;
                    return;
                }
            }
        } elseif (is_array($mappingDefinition)) {
            if (reset($mappingDefinition) === null) {
                $mappingDefinition[0] = $inst;
                call_user_func_array($mappingDefinition, [$propertyValue]);
                return;
            } elseif (reset($mappingDefinition) === $this) {
                call_user_func_array($mappingDefinition, [$propertyValue, $inst]);
                return;
            }
        }
        throw new \yii\base\ErrorException('Could not set property ' . $propertyName . ' in wrapped object');
    }

    /**
     * @param object $inst
     */
    protected function setInstProperites($inst, array $mappings)
    {
        foreach ($mappings as $cpn => $mapping) {
            $this->setInstProperty($inst, $cpn, $mapping, $this->$cpn);
        }
        foreach ($this->addInstProperties as $p => $v) {
            $this->setInstProperty($inst, $p, true, $v);
        }
    }

    /**
     * Creates the wrapped instance
     * @return object
     */
    protected function createNewInst()
    {
        $classReflection = new \ReflectionClass($this->instClass);
        if ($this->constructorArgs === null) {
            return $classReflection->newInstance();
        } else {
            return $classReflection->newInstanceArgs($this->concstructorArgs);
        }
    }

    /**
     * Destroys the object
     *
     * @param object $inst Instance to destroy
     */
    protected function destroyInst($inst)
    {
        unset($this->$inst);
        $this->_instInitFinalized = false;
    }

    protected function afterNewInst()
    {
        $this->trigger(static::EVENT_AFTER_NEW_INST, new InstanceWrapperComponentEvent(['inst' => $this->_inst]));
    }

    protected function doCreateNewInst()
    {
        $this->_instInitFinalized = false;
        $this->_inst              = $this->createNewInst();
        $this->afterNewInst();
        $this->setInstProperites($this->_inst, $this->getInstPropertyMappings());
        $this->_instInitFinalized = true;
    }

    /**
     * Returns the wrapped object instance
     *
     * @return object Wrapped object
     *
     */
    public function getInst()
    {
        if (!isset($this->_inst)) {
            $this->doCreateNewInst();
        }
        return $this->_inst;
    }

    public function hasInst($allowUnfinalizedInst = false)
    {
        return $this->_inst !== null && ($allowUnfinalizedInst || $this->_instInitFinalized);
    }

}


<?php declare(strict_types=1);

namespace Identity;

use DateTime;
use dibi;
use Dibi\Connection;
use Dibi\IDataSource;
use Nette\Security\Passwords;
use Nette\SmartObject;

/**
 * Class IdentityModel
 *
 * @author  geniv
 * @package Identity
 */
class IdentityModel implements IIdentityModel
{
    use SmartObject;

    // define constant table names
    const
        TABLE_NAME = 'identity',
        NO_TIME = 'nostrtotime';

    const
        ID_SEPARATOR = '.|.',
        TIME_SEPARATOR = '.X|.',
        PART_SEPARATOR = '_|_';

    const
        COLUMN_ID = 'id',
        REQUIRE_COLUMNS = [self::COLUMN_ID, 'login', 'hash', 'role', 'active',];    //id, login, hash, role, active

    /** @var Connection */
    private $connection;
    /** @var string */
    private $tableName, $tableIdentity;
    /** @var array */
    private $columns = ['id', 'login', 'hash', 'username', 'email', 'role', 'active', 'added'];

    /**
     * IdentityModel constructor.
     *
     * @param string     $prefix
     * @param string     $tableName
     * @param Connection $connection
     */
    public function __construct(string $prefix, string $tableName = self::TABLE_NAME, Connection $connection)
    {
        $this->tableName = $tableName;
        $this->connection = $connection;
        // define table names
        $this->tableIdentity = $prefix . $tableName;
    }

    /**
     * Get columns.
     *
     * @return array
     */
    public function getColumns(): array
    {
        return $this->columns;
    }

    /**
     * Add column.
     *
     * @param string $name
     * @return $this
     */
    public function addColumn(string $name)
    {
        $this->columns[] = $name;
        return $this;
    }

    /**
     * Set columns.
     *
     * @param array $columns
     * @return $this
     * @throws IdentityException
     */
    public function setColumns(array $columns)
    {
        $diff = array_diff(self::REQUIRE_COLUMNS, $columns);
        if ($diff) {
            throw new IdentityException('This column(s) are required: "' . implode('", "', $diff) . '" !');
        }
        $this->columns = $columns;
        return $this;
    }

    /**
     * Get list.
     *
     * @return IDataSource
     */
    public function getList(): IDataSource
    {
        $columns = array_map(function ($item) {
            return $this->tableName[0] . '.' . $item;
        }, $this->columns);
        return $this->connection->select($columns)->from($this->tableIdentity)->as($this->tableName[0]);
    }

    /**
     * Get by id.
     *
     * @param int $id
     * @return \Dibi\Row|false
     */
    public function getById(int $id)
    {
        /** @noinspection PhpUndefinedMethodInspection */
        return $this->getList()
            ->where([$this->tableName[0] . '.' . self::COLUMN_ID => $id])
            ->fetch();
    }

    /**
     * Get by email.
     *
     * @param string $email
     * @return \Dibi\Row|false
     */
    public function getByEmail(string $email)
    {
        // get by id and active must by true
        /** @noinspection PhpUndefinedMethodInspection */
        return $this->getList()
            ->where([$this->tableName[0] . '.email' => $email, $this->tableName[0] . '.active' => true])
            ->fetch();
    }

    /**
     * Insert.
     *
     * @param array $values
     * @return int
     * @throws \Dibi\Exception
     */
    public function insert(array $values): int
    {
        $values['added%sql'] = 'NOW()';
        $values['hash'] = $this->getHash($values['password']);  // auto hash password
        unset($values['password']);

        $res = $this->connection->insert($this->tableIdentity, $values)->execute(Dibi::IDENTIFIER);
        return $res;
    }

    /**
     * Update.
     *
     * @param int   $id
     * @param array $values
     * @return bool
     * @throws \Dibi\Exception
     */
    public function update(int $id, array $values): bool
    {
        if (isset($values['password'])) {
            if ($values['password']) {
                $values['hash'] = $this->getHash($values['password']);  // auto hash password
            }
            unset($values['password']);
        }

        $res = (bool) $this->connection->update($this->tableIdentity, $values)->where([self::COLUMN_ID => $id])->execute();
        return $res;
    }

    /**
     * Delete.
     *
     * @param int $id
     * @return bool
     * @throws \Dibi\Exception
     */
    public function delete(int $id): bool
    {
        $res = (bool) $this->connection->delete($this->tableIdentity)->where([self::COLUMN_ID => $id])->execute();
        return $res;
    }

    /**
     * Get hash.
     *
     * @param string $password
     * @return string
     */
    public function getHash(string $password): string
    {
        return Passwords::hash($password);
    }

    /**
     * Verify hash.
     *
     * @param string $password
     * @param string $hash
     * @return bool
     */
    public function verifyHash(string $password, string $hash): bool
    {
        return Passwords::verify($password, $hash);
    }

    /**
     * Exist login.
     *
     * @param string $login
     * @return int
     */
    public function existLogin(string $login): int
    {
        return (int) $this->connection->select(self::COLUMN_ID)
            ->from($this->tableIdentity)
            ->where(['login' => $login])
            ->fetchSingle();
    }

    /**
     * Exist email.
     *
     * @param string $email
     * @return int
     */
    public function existEmail(string $email): int
    {
        return (int) $this->connection->select(self::COLUMN_ID)
            ->from($this->tableIdentity)
            ->where(['email' => $email])
            ->fetchSingle();
    }

    /**
     * Clean user.
     *
     * @param string|null $validate
     * @return int
     * @throws \Dibi\Exception
     * @throws \Exception
     */
    public function cleanUser(string $validate = null): int
    {
        $result = 0;
        if ($validate) {
            $validateTo = new DateTime;
            $validateTo->modify($validate);

            /** @noinspection PhpUndefinedMethodInspection */
            $list = $this->getList()
                ->where([
                    $this->tableName[0] . '.active' => false,
                    $this->tableName[0] . '.added IS NOT NULL',
                    [$this->tableName[0] . '.added<=%dt', $validateTo],
                ]);

            foreach ($list as $item) {
                if ($this->delete($item[self::COLUMN_ID])) {
                    $result++;
                }
            }
        }
        return $result;
    }

    /**
     * Get encode hash.
     *
     * @param int         $id
     * @param string      $slug
     * @param string|null $linkValidate
     * @return string
     */
    public function getEncodeHash(int $id, string $slug, string $linkValidate = null): string
    {
        return base64_encode(uniqid(($linkValidate ? strtotime($linkValidate) : self::NO_TIME) . self::TIME_SEPARATOR, true) . self::PART_SEPARATOR . $this->getHash($id . $slug) . self::ID_SEPARATOR . $id);
    }

    /**
     * Get decode link.
     *
     * @param string $hash
     * @return array
     * @throws IdentityException
     * @throws \Exception
     */
    public function getDecodeHash(string $hash): array
    {
        $decode = base64_decode($hash);
        list($part1, $part2) = explode(self::PART_SEPARATOR, $decode);

        $p1 = explode(self::TIME_SEPARATOR, $part1);
        list($linkValidate,) = $p1; // get validate int

        $id = null;
        $verifyHash = null;
        $dateNow = new DateTime();
        $dateValidate = $dateNow;
        if ($linkValidate == self::NO_TIME) {
            // parameter $linkValidate is null -> generate now
            $linkValidate = $dateNow->getTimestamp();
        }
        $dateValidate->setTimestamp((int) $linkValidate);  // convert validate int do datetime
        if ($dateValidate >= $dateNow) {   // check expiration
            $p2 = explode(self::ID_SEPARATOR, $part2);
            $verifyHash = implode('.', array_slice($p2, 0, -1));  // regenerate hash
            $id = $p2[count($p2) - 1];  // get id from last part
        } else {
            throw new IdentityException('Activate link is expired!');
        }
        return ['id' => $id, 'verifyHash' => $verifyHash, 'expired' => (int) $linkValidate];
    }

    /**
     * Process approve.
     *
     * @param string $hash
     * @return bool
     * @throws IdentityException
     * @throws \Dibi\Exception
     */
    public function processApprove(string $hash): bool
    {
        $decode = $this->getDecodeHash($hash);

        $id = (int) $decode['id'];
        $verifyHash = $decode['verifyHash'];

        $item = $this->getById($id);  // load row from db
        if ($item && $id == $item['id']) { // not null result
            if (!$item['active']) {
                if ($this->verifyHash($item['id'] . $item['login'], $verifyHash)) {  // check hash and password
                    return $this->update($item['id'], ['active' => true]);    // final activate user
                } else {
                    throw new IdentityException('Invalid hash!');
                }
            } else {
                // basic check expire link (after click), next check is only active status not datetime! - execute update only if not active
                throw new IdentityException('User is already approve!');
            }
        } else {
            throw new IdentityException('User does not exist!');
        }
    }

    /**
     * Is valid forgotten.
     *
     * @param string $hash
     * @return bool
     * @throws IdentityException
     */
    public function isValidForgotten(string $hash): bool
    {
        $decode = $this->getDecodeHash($hash);

        $id = (int) $decode['id'];
        $verifyHash = $decode['verifyHash'];

        $item = $this->getById($id);  // load row from db
        if ($item && $id == $item['id']) { // not null result
            // check hash and password
            return $this->verifyHash($item['id'] . $item['login'], $verifyHash);
        }
        return false;
    }

    /**
     * Process forgotten.
     *
     * @param string $hash
     * @param string $password
     * @return bool
     * @throws IdentityException
     * @throws \Dibi\Exception
     */
    public function processForgotten(string $hash, string $password): bool
    {
        $decode = $this->getDecodeHash($hash);

        $id = (int) $decode['id'];
        $verifyHash = $decode['verifyHash'];

        $item = $this->getById($id);  // load row from db
        if ($item && $id == $item['id']) { // not null result
            $values['hash'] = $this->getHash($password);  // auto hash password
            if ($this->verifyHash($item['id'] . $item['login'], $verifyHash)) {  // check hash and password
                return $this->update($item['id'], $values);    // final save user
            } else {
                throw new IdentityException('Invalid hash!');
            }
        } else {
            throw new IdentityException('User does not exist!');
        }
    }
}


<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace yii\web;

use Yii;
use yii\base\Exception;
use yii\base\ErrorException;
use yii\base\UserException;
use yii\helpers\VarDumper;

/**
 * ErrorHandler handles uncaught PHP errors and exceptions.
 *
 * ErrorHandler displays these errors using appropriate views based on the
 * nature of the errors and the mode the application runs at.
 *
 * ErrorHandler is configured as an application component in [[\yii\base\Application]] by default.
 * You can access that instance via `Yii::$app->errorHandler`.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @author Timur Ruziev <resurtm@gmail.com>
 * @since 2.0
 */
class ErrorHandler extends \yii\base\ErrorHandler
{
    /**
     * @var integer maximum number of source code lines to be displayed. Defaults to 19.
     */
    public $maxSourceLines = 19;
    /**
     * @var integer maximum number of trace source code lines to be displayed. Defaults to 13.
     */
    public $maxTraceSourceLines = 13;
    /**
     * @var string the route (e.g. 'site/error') to the controller action that will be used
     * to display external errors. Inside the action, it can retrieve the error information
     * using `Yii::$app->errorHandler->exception. This property defaults to null, meaning ErrorHandler
     * will handle the error display.
     */
    public $errorAction;
    /**
     * @var string the path of the view file for rendering exceptions without call stack information.
     */
    public $errorView = '@yii/views/errorHandler/error.php';
    /**
     * @var string the path of the view file for rendering exceptions.
     */
    public $exceptionView = '@yii/views/errorHandler/exception.php';
    /**
     * @var string the path of the view file for rendering exceptions and errors call stack element.
     */
    public $callStackItemView = '@yii/views/errorHandler/callStackItem.php';
    /**
     * @var string the path of the view file for rendering previous exceptions.
     */
    public $previousExceptionView = '@yii/views/errorHandler/previousException.php';
    /**
     * @var array list of the PHP predefined variables that should be displayed on the error page.
     * Note that a variable must be accessible via `$GLOBALS`. Otherwise it won't be displayed.
     * Defaults to `['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION']`.
     * @see renderRequest()
     * @since 2.0.7
     */
    public $displayVars = ['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION'];

    /**
     * Renders the exception.
     * @param \Exception $exception the exception to be rendered.
     */
    protected function renderException($exception)
    {
        if (Yii::$app->has('response')) {
            $response = Yii::$app->getResponse();
            // reset parameters of response to avoid interference with partially created response data
            // in case the error occurred while sending the response.
            $response->isSent = false;
            $response->stream = null;
            $response->data = null;
            $response->content = null;
        } else {
            $response = new Response();
        }

        $useErrorView = $response->format === Response::FORMAT_HTML && (!YII_DEBUG || $exception instanceof UserException);

        if ($useErrorView && $this->errorAction !== null) {
            $result = Yii::$app->runAction($this->errorAction);
            if ($result instanceof Response) {
                $response = $result;
            } else {
                $response->data = $result;
            }
        } elseif ($response->format === Response::FORMAT_HTML) {
            if (YII_ENV_TEST || isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
                // AJAX request
                $response->data = '
' . $this->htmlEncode(static::convertExceptionToString($exception)) . '
';
            } else {
                // if there is an error during error rendering it's useful to
                // display PHP error in debug mode instead of a blank screen
                if (YII_DEBUG) {
                    ini_set('display_errors', 1);
                }
                $file = $useErrorView ? $this->errorView : $this->exceptionView;
                $response->data = $this->renderFile($file, [
                    'exception' => $exception,
                ]);
            }
        } elseif ($response->format === Response::FORMAT_RAW) {
            $response->data = static::convertExceptionToString($exception);
        } else {
            $response->data = $this->convertExceptionToArray($exception);
        }

        if ($exception instanceof HttpException) {
            $response->setStatusCode($exception->statusCode);
        } else {
            $response->setStatusCode(500);
        }

        $response->send();
    }

    /**
     * Converts an exception into an array.
     * @param \Exception $exception the exception being converted
     * @return array the array representation of the exception.
     */
    protected function convertExceptionToArray($exception)
    {
        if (!YII_DEBUG && !$exception instanceof UserException && !$exception instanceof HttpException) {
            $exception = new HttpException(500, 'There was an error at the server.');
        }

        $array = [
            'name' => ($exception instanceof Exception || $exception instanceof ErrorException) ? $exception->getName() : 'Exception',
            'message' => $exception->getMessage(),
            'code' => $exception->getCode(),
        ];
        if ($exception instanceof HttpException) {
            $array['status'] = $exception->statusCode;
        }
        if (YII_DEBUG) {
            $array['type'] = get_class($exception);
            if (!$exception instanceof UserException) {
                $array['file'] = $exception->getFile();
                $array['line'] = $exception->getLine();
                $array['stack-trace'] = explode("\n", $exception->getTraceAsString());
                if ($exception instanceof \yii\db\Exception) {
                    $array['error-info'] = $exception->errorInfo;
                }
            }
        }
        if (($prev = $exception->getPrevious()) !== null) {
            $array['previous'] = $this->convertExceptionToArray($prev);
        }

        return $array;
    }

    /**
     * Converts special characters to HTML entities.
     * @param string $text to encode.
     * @return string encoded original text.
     */
    public function htmlEncode($text)
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }

    /**
     * Adds informational links to the given PHP type/class.
     * @param string $code type/class name to be linkified.
     * @return string linkified with HTML type/class name.
     */
    public function addTypeLinks($code)
    {
        if (preg_match('/(.*?)::([^(]+)/', $code, $matches)) {
            $class = $matches[1];
            $method = $matches[2];
            $text = $this->htmlEncode($class) . '::' . $this->htmlEncode($method);
        } else {
            $class = $code;
            $method = null;
            $text = $this->htmlEncode($class);
        }

        $url = $this->getTypeUrl($class, $method);

        if (!$url) {
            return $text;
        }

        return '' . $text . '';
    }

    /**
     * Returns the informational link URL for a given PHP type/class.
     * @param string $class the type or class name.
     * @param string|null $method the method name.
     * @return string|null the informational link URL.
     * @see addTypeLinks()
     */
    protected function getTypeUrl($class, $method)
    {
        if (strpos($class, 'yii\\') !== 0) {
            return null;
        }

        $page = $this->htmlEncode(strtolower(str_replace('\\', '-', $class)));
        $url = "http://www.yiiframework.com/doc-2.0/$page.html";
        if ($method) {
            $url .= "#$method()-detail";
        }

        return $url;
    }

    /**
     * Renders a view file as a PHP script.
     * @param string $_file_ the view file.
     * @param array $_params_ the parameters (name-value pairs) that will be extracted and made available in the view file.
     * @return string the rendering result
     */
    public function renderFile($_file_, $_params_)
    {
        $_params_['handler'] = $this;
        if ($this->exception instanceof ErrorException || !Yii::$app->has('view')) {
            ob_start();
            ob_implicit_flush(false);
            extract($_params_, EXTR_OVERWRITE);
            require(Yii::getAlias($_file_));

            return ob_get_clean();
        } else {
            return Yii::$app->getView()->renderFile($_file_, $_params_, $this);
        }
    }

    /**
     * Renders the previous exception stack for a given Exception.
     * @param \Exception $exception the exception whose precursors should be rendered.
     * @return string HTML content of the rendered previous exceptions.
     * Empty string if there are none.
     */
    public function renderPreviousExceptions($exception)
    {
        if (($previous = $exception->getPrevious()) !== null) {
            return $this->renderFile($this->previousExceptionView, ['exception' => $previous]);
        } else {
            return '';
        }
    }

    /**
     * Renders a single call stack element.
     * @param string|null $file name where call has happened.
     * @param integer|null $line number on which call has happened.
     * @param string|null $class called class name.
     * @param string|null $method called function/method name.
     * @param array $args array of method arguments.
     * @param integer $index number of the call stack element.
     * @return string HTML content of the rendered call stack element.
     */
    public function renderCallStackItem($file, $line, $class, $method, $args, $index)
    {
        $lines = [];
        $begin = $end = 0;
        if ($file !== null && $line !== null) {
            $line--; // adjust line number from one-based to zero-based
            $lines = @file($file);
            if ($line < 0 || $lines === false || ($lineCount = count($lines)) < $line) {
                return '';
            }

            $half = (int) (($index === 1 ? $this->maxSourceLines : $this->maxTraceSourceLines) / 2);
            $begin = $line - $half > 0 ? $line - $half : 0;
            $end = $line + $half < $lineCount ? $line + $half : $lineCount - 1;
        }

        return $this->renderFile($this->callStackItemView, [
            'file' => $file,
            'line' => $line,
            'class' => $class,
            'method' => $method,
            'index' => $index,
            'lines' => $lines,
            'begin' => $begin,
            'end' => $end,
            'args' => $args,
        ]);
    }

    /**
     * Renders the global variables of the request.
     * List of global variables is defined in [[displayVars]].
     * @return string the rendering result
     * @see displayVars
     */
    public function renderRequest()
    {
        $request = '';
        foreach ($this->displayVars as $name) {
            if (!empty($GLOBALS[$name])) {
                $request .= '$' . $name . ' = ' . VarDumper::export($GLOBALS[$name]) . ";\n\n";
            }
        }

        return '
' . rtrim($request, "\n") . '
';
    }

    /**
     * Determines whether given name of the file belongs to the framework.
     * @param string $file name to be checked.
     * @return boolean whether given name of the file belongs to the framework.
     */
    public function isCoreFile($file)
    {
        return $file === null || strpos(realpath($file), YII2_PATH . DIRECTORY_SEPARATOR) === 0;
    }

    /**
     * Creates HTML containing link to the page with the information on given HTTP status code.
     * @param integer $statusCode to be used to generate information link.
     * @param string $statusDescription Description to display after the the status code.
     * @return string generated HTML with HTTP status code information.
     */
    public function createHttpStatusLink($statusCode, $statusDescription)
    {
        return 'HTTP ' . (int) $statusCode . ' – ' . $statusDescription . '';
    }

    /**
     * Creates string containing HTML link which refers to the home page of determined web-server software
     * and its full name.
     * @return string server software information hyperlink.
     */
    public function createServerInformationLink()
    {
        $serverUrls = [
            'http://httpd.apache.org/' => ['apache'],
            'http://nginx.org/' => ['nginx'],
            'http://lighttpd.net/' => ['lighttpd'],
            'http://gwan.com/' => ['g-wan', 'gwan'],
            'http://iis.net/' => ['iis', 'services'],
            'http://php.net/manual/en/features.commandline.webserver.php' => ['development'],
        ];
        if (isset($_SERVER['SERVER_SOFTWARE'])) {
            foreach ($serverUrls as $url => $keywords) {
                foreach ($keywords as $keyword) {
                    if (stripos($_SERVER['SERVER_SOFTWARE'], $keyword) !== false) {
                        return '' . $this->htmlEncode($_SERVER['SERVER_SOFTWARE']) . '';
                    }
                }
            }
        }

        return '';
    }

    /**
     * Creates string containing HTML link which refers to the page with the current version
     * of the framework and version number text.
     * @return string framework version information hyperlink.
     */
    public function createFrameworkVersionLink()
    {
        return '' . $this->htmlEncode(Yii::getVersion()) . '';
    }

    /**
     * Converts arguments array to its string representation
     *
     * @param array $args arguments array to be converted
     * @return string string representation of the arguments array
     */
    public function argumentsToString($args)
    {
        $count = 0;
        $isAssoc = $args !== array_values($args);

        foreach ($args as $key => $value) {
            $count++;
            if ($count>=5) {
                if ($count>5) {
                    unset($args[$key]);
                } else {
                    $args[$key] = '...';
                }
                continue;
            }

            if (is_object($value)) {
                $args[$key] = '' . $this->htmlEncode(get_class($value)) . '';
            } elseif (is_bool($value)) {
                $args[$key] = '' . ($value ? 'true' : 'false') . '';
            } elseif (is_string($value)) {
                $fullValue = $this->htmlEncode($value);
                if (mb_strlen($value, 'UTF-8') > 32) {
                    $displayValue = $this->htmlEncode(mb_substr($value, 0, 32, 'UTF-8')) . '...';
                    $args[$key] = "'$displayValue'";
                } else {
                    $args[$key] = "'$fullValue'";
                }
            } elseif (is_array($value)) {
                $args[$key] = '[' . $this->argumentsToString($value) . ']';
            } elseif ($value === null) {
                $args[$key] = 'null';
            } elseif (is_resource($value)) {
                $args[$key] = 'resource';
            } else {
                $args[$key] = '' . $value . '';
            }

            if (is_string($key)) {
                $args[$key] = '\'' . $this->htmlEncode($key) . "' => $args[$key]";
            } elseif ($isAssoc) {
                $args[$key] = "$key => $args[$key]";
            }
        }
        $out = implode(', ', $args);

        return $out;
    }

    /**
     * Returns human-readable exception name
     * @param \Exception $exception
     * @return string human-readable exception name or null if it cannot be determined
     */
    public function getExceptionName($exception)
    {
        if ($exception instanceof \yii\base\Exception || $exception instanceof \yii\base\InvalidCallException || $exception instanceof \yii\base\InvalidParamException || $exception instanceof \yii\base\UnknownMethodException) {
            return $exception->getName();
        }
        return null;
    }
}


<?php
/*
This is part of Wedeto, the WEb DEvelopment TOolkit.
It is published under the MIT Open Source License.

Copyright 2017-2018, Egbert van der Wal

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

namespace Wedeto\Application;

use Wedeto\Resolve\Autoloader;
use Wedeto\Util\Functions as WF;
use Wedeto\IO\Path;
use Wedeto\IO\PermissionError;
use Wedeto\IO\IOException;
use Wedeto\Util\DI\InjectionTrait;

class PathConfig
{
    use InjectionTrait;

    /** Path where Composer stores the dependencies - obtained from Composer autoloader */
    protected $vendor_dir = null;

    /** Root path. Defaults to the parent directory of the Composer vendor directory */
    protected $root;

    /** Path where configuration files are stored. Defaults to root/config */
    protected $config;

    /** Path where web server serves the files of the Wedeto application. Defaults to root/webroot */
    protected $webroot;

    /** Path where web server serves the asset files. Defaults to webroot/assets */
    protected $assets;

    /** Path where Wedeto should have write access to store temporary files. Defaults to root/var */
    protected $var;

    /** Path where Wedeto should have write access to store cache files. Defaults to var/cache */
    protected $cache;

    /** Path where Wedeto should have write access to store log files. Defaults to var/log */
    protected $log;

    /** Path where Wedeto should have write access to store uploaded files. Defaults to root/uploads */
    protected $uploads;

    /** Whether the current path configuration has been validated */
    protected $path_checked = false;

    /** If the script is run from CLI or through the web */
    protected $cli;

    /**
     * Construct the PathConfig object. Pass in a root path,
     * or an array of path element => path pairs.
     *
     * @param string|array $root The root path or a set of path elements. May
     *                           be omitted to do auto-configuration. When using
     *                           composer, this means the root will be set to the
     *                           parent path of the Composer vendor directory.
     */
    public function __construct($root = null)
    {
        if (is_string($root))
            $root = ['root' => $root];

        if (WF::is_array_like($root))
        {
            // Validate values
            foreach ($root as $key => $path)
            {
                if (!property_exists($this, $key))
                    throw new \InvalidArgumentException("Invalid path element: " . $key);
                if (!is_string($path) || empty($path))
                    throw new \InvalidArgumentException("Invalid path: " . WF::str($path));
                if (!file_exists($path))
                    throw new IOException("Path '$key' does not exist: " . $path);
                if (!is_dir($path))
                    throw new IOException("Path is not a directory: " . $path);
            }

            extract($root);
        }
        elseif (!empty($root))
            throw new \InvalidArgumentException("Invalid path: " . WF::str($root));

        $SEP = DIRECTORY_SEPARATOR;
        $composer_class = Autoloader::findComposerAutoloader();
        if (!empty($composer_class))
        {
            $this->vendor_dir = Path::realpath(Autoloader::findComposerAutoloaderVendorDir($composer_class));
            $this->root = $root ?? dirname($this->vendor_dir);
        }
        else
        {
            // @codeCoverageIgnoreStart
            // PHPUnit is run using Composer
            $this->root = $root ?? Path::realpath(dirname($_SERVER['SCRIPT_FILENAME']));
            // @codeCoverageIgnoreEnd
        }

        $this->cli = PHP_SAPI === 'cli';
        if ($this->cli || PHP_SAPI === 'cli-server')
        {
            $this->webroot = $webroot ?? $this->root . $SEP . 'http';
        }
        else
        {
            // @codeCoverageIgnoreStart
            // PHPUnit is run in CLI
            $this->webroot = $webroot ?? Path::realpath(dirname($_SERVER['SCRIPT_FILENAME']));
            // @codeCoverageIgnoreEnd
        }

        $this->config = $config ?? $this->root . $SEP . 'config';
        $this->var = $var ?? $this->root . $SEP . 'var';
        $this->uploads = $uploads ?? $this->root . $SEP . 'uploads';

        $this->assets = $assets ?? $this->webroot . $SEP . 'assets';

        $this->log = $log ?? $this->var . $SEP . 'log';
        $this->cache = $cache ?? $this->var . $SEP . 'cache';
    }

    /**
     * Validate path configuration.
     * @return bool True when the path configuration is ok
     * @throws IOException When a path element does not exist
     * @throws PermissionException When permissions are incorrect
     */
    public function checkPaths()
    {
        if ($this->path_checked)
            return true;

        foreach (array('root', 'webroot') as $type)
        {
            $path = $this->$type;
            if (!file_exists($path) || !is_dir($path))
                throw new IOException("Path '$type' does not exist: " . $path);

            if (!is_readable($path))
                throw new PermissionError($path, "Path '$type' cannot be read");
        }

        if (!is_dir($this->config) || is_readable($this->config))
            $this->config = null;

        foreach (array('var', 'cache', 'log', 'uploads') as $write_dir)
        {
            $path = $this->$write_dir;
            if (!is_dir($path))
            {
                $dn = dirname($path);
                if (!file_exists($path) && $dn === $this->var)
                {
                    // The parent directory is var/ and must be writeable as
                    // this was checked earlier in this loop.
                    Path::mkdir($path);
                }
                else
                {
                    if (file_exists($path))
                        throw new IOException("Path '$write_dir' exists but is not a directory: " . $path);
                    $this->$write_dir = null;
                    continue;
                }
            }

            if (!is_writable($path))
            {
                try
                {
                    // We can try to fix permissions if we own the file
                    Path::makeWritable($path);
                }
                catch (PermissionError $e)
                {
                    $this->$write_dir = null;
                    if ($this->cli)
                        WF::debug("Failed to get write access to: %s", $e->getMessage());
                }
            }
        }
        $this->path_checked = true;
        return true;
    }

    /**
     * Get any of the path elements - magic method
     *
     * @param string $field The path to get
     * @return string The path to the requested path element
     */
    public function __get($field)
    {
        if (!property_exists($this, $field))
            throw new \InvalidArgumentException("Invalid path: $field");

        return $this->$field;
    }

    /**
     * Set any of the path elements - magic method
     *
     * @param string $field The path to set
     * @param string $value The path for this path element
     */
    public function __set($field, $value)
    {
        if (!is_string($value))
            throw new \InvalidArgumentException("Invalid path: " . WF::str($value));

        if (!property_exists($this, $field))
            throw new \InvalidArgumentException("Invalid path element: $field");

        $this->$field = $value;
    }
}


<?php

/**
 * @file
 * Contains \Commercie\Payment\EventDispatcherCollection.
 */

namespace Commercie\Payment;

use Commercie\Payment\Payment\PaymentInterface;
use Commercie\Payment\PaymentMethod\PaymentMethodInterface;

/**
 * Dispatches events to a collection of event dispatchers.
 */
class EventDispatcherCollection implements EventDispatcherInterface
{

    /**
     * The event dispatchers.
     *
     * @var \Commercie\Payment\EventDispatcherInterface[]
     */
    protected $eventDispatchers = [];

    /**
     * Adds an event dispatcher to the collection.
     *
     * @param \Commercie\Payment\EventDispatcherInterface $eventDispatcher
     *
     * @return $this
     */
    public function addEventDispatcher(
      EventDispatcherInterface $eventDispatcher
    ) {
        $this->eventDispatchers[] = $eventDispatcher;

        return $this;
    }

    /**
     * Aggregates a list of ternary values.
     *
     * @param bool[]|null[] $values
     *
     * @return bool|null
     */
    protected function aggregateTernaryValues(array $values)
    {
        if (in_array(false, $values, true)) {
            return false;
        } elseif (in_array(true, $values, true)) {
            return true;
        } else {
            return null;
        }
    }

    public function postPaymentStatusChange(PaymentInterface $payment)
    {
        foreach ($this->eventDispatchers as $eventDispatcher) {
            $eventDispatcher->postPaymentStatusChange($payment);
        }
    }

    public function canExecutePayment(PaymentMethodInterface $paymentMethod)
    {
        $results = [];
        foreach ($this->eventDispatchers as $eventDispatcher) {
            $results[] = $eventDispatcher->canExecutePayment($paymentMethod);
        }

        return $this->aggregateTernaryValues($results);
    }

    public function preExecutePayment(PaymentMethodInterface $paymentMethod)
    {
        foreach ($this->eventDispatchers as $eventDispatcher) {
            $eventDispatcher->preExecutePayment($paymentMethod);
        }
    }

    public function canCapturePayment(PaymentMethodInterface $paymentMethod)
    {
        $results = [];
        foreach ($this->eventDispatchers as $eventDispatcher) {
            $results[] = $eventDispatcher->canCapturePayment($paymentMethod);
        }

        return $this->aggregateTernaryValues($results);
    }

    public function preCapturePayment(PaymentMethodInterface $paymentMethod)
    {
        foreach ($this->eventDispatchers as $eventDispatcher) {
            $eventDispatcher->preCapturePayment($paymentMethod);
        }
    }

    public function canRefundPayment(PaymentMethodInterface $paymentMethod)
    {
        $results = [];
        foreach ($this->eventDispatchers as $eventDispatcher) {
            $results[] = $eventDispatcher->canRefundPayment($paymentMethod);
        }

        return $this->aggregateTernaryValues($results);
    }

    public function preRefundPayment(PaymentMethodInterface $paymentMethod)
    {
        foreach ($this->eventDispatchers as $eventDispatcher) {
            $eventDispatcher->preRefundPayment($paymentMethod);
        }
    }

}


<?php
namespace ErrorStream\ErrorStreamClient;

use ErrorStream\ErrorStreamClient\ErrorStreamReport;

/**
 * ErrorStreamClient allows multiple different interfaces to send data to ErrorStream.com using
 * eithter exceptions or the ErrorStreamReport class.
 * Class ErrorStreamClient
 * @package ErrorStream\ErrorStreamClient
 */
class ErrorStreamClient {

    public $api_token;
    public $project_token;
    private $tags = [];
    private $context = [];

    /**
     * Tag this software. Good things to tag might be releases, servers, etc.
     * This will be appended to any reports that arise.
     * @param $tag
     */
    public function addTag($tag)
    {
        $this->tags[] = $tag;
    }

    /**
     * Add context to your application stack trace. Pass a string to represent
     * more details in your stack traces within the website. This will be appended
     * to any reports that arise.
     * @param $context
     */
    public function addContext($context)
    {
        $this->context[] = $context;
    }

    /**
     * Report an exception to the errorstream website.
     * @param \Exception $ex
     * @return string
     */
    public function reportException(\Exception $ex)
    {
        $report = new ErrorStreamReport();
        $report->error_group = $ex->getMessage().':'.$ex->getLine();
        $report->line_number = $ex->getLine();
        $report->file_name = $ex->getFile();
        $report->message = $ex->getMessage();
        $report->stack_trace = $ex->getTraceAsString();
        $report->severity = 3;
        return $this->report($report);
    }

    /**
     * Make a request sending the errorstream report.
     * @param \ErrorStream\ErrorStreamClient\ErrorStreamReport $report
     * @return string
     */
    public function report(ErrorStreamReport $report)
    {
        $report->tags = $this->tags;
        $report->context = $this->context;
        return $this->makeRequest($report);
    }

    /**
     * Simple request interface using guzzle to send JSON data to errorstream
     * @param $data
     * @return string
     */
    protected function makeRequest($data)
    {
        $url = 'https://www.errorstream.com/api/1.0/errors/create?'.http_build_query(['api_token' => $this->api_token, 'project_token' => $this->project_token]);

        try {
            $ch = curl_init();

            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
            curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

            $result = curl_exec($ch);
            curl_close($ch);

            return $result;

        } catch (\Exception $ex){
            return $ex->getMessage();
        }
    }

}


<?php

/**
 * This file is part of the Minion package.
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @license MIT License
 */

namespace Minion;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\HttpFoundation\Request;

/**
 * Class Console.
 *
 * @package Minion
 * @author Damian Szczerbiński <dszczer@gmail.com>
 */
class Console extends Application
{
    /** @var  string Command path */
    public $commandPath;

    /**
     * Console constructor.
     *
     * @param string $appNamespace Application namespace (autodetect, if empty)
     * @param array  $values       Default options
     * @param array  $fixPaths     Path fixes
     *
     * @return Console
     */
    public function __construct($appNamespace = '', array $values = [], array $fixPaths = []) {
        parent::__construct($appNamespace, $values, $fixPaths);

        $this->commandPath = isset($fixPaths['commandDir'])
            ? $fixPaths['commandDir']
            : Utils::fixPath($this->getRootDir() . '/src/Command/');

        $this->loadCommands($this->getCommandPath());

        // load Propel commands
        if($this['minion.usePropel']) {
            $this->loadCommands(Utils::fixPath(($this['environment'] === 'test' ? $this->getRootDir() . '/../'
                    : $this->getRootDir()) . '/vendor/propel/propel/src/Propel/Generator/Command/'), 'propel'
            );
            // replace default config-dir value command option
            /** @var Command $command */
            foreach($this['console']->all('propel') as $command) {
                $def = $command->getDefinition();
                $old = $def->getOptions();
                foreach($old as $i => $o)
                    if($o->getName() === 'config-dir') {
                        unset($old[$i]);
                        break;
                    }
                $opt = new InputOption('config-dir', null, InputOption::VALUE_REQUIRED,
                    'The directory where the configuration file is placed.', $this->getPackageDir());
                $def->setOptions(\array_merge($old, [$opt]));
            }
        }
    }

    /**
     * Get project command directory path.
     *
     * @return string
     */
    public function getCommandPath() {
        return $this->commandPath;
    }

    /**
     * Run console.
     *
     * @param Request $request Request object (not used in commands). Only to match inheritance requirements.
     *
     * @return mixed
     */
    public function run(Request $request = null) {
        $this->boot();

        return $this['console']->run();
    }

    /**
     * Load all Command files from $path directory.
     *
     * Traverse all files inside specified directory, but loads only those files with suffix 'Command.php'.
     *
     * @param string      $path   Directory with Command files
     * @param string|null $prefix Commands namespace
     *
     * @return void
     */
    public function loadCommands($path, $prefix = null) {
        // load commands
        try {
            $iterator = new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS);

            /** @var \RecursiveDirectoryIterator $child */
            foreach($iterator as $child)
                if($child->isFile() && \preg_match('/Command.php$/', $child->getBasename())) {
                    $namespace = $class = "";
                    $gettingNamespace = $gettingClass = false;
                    foreach(\token_get_all(\file_get_contents($child->getRealPath())) as $token) {
                        if(\is_array($token) && ($token[0] === T_ABSTRACT || $token[0] === T_INTERFACE)) {
                            $namespace = $class = '';
                            break;
                        }
                        if(\is_array($token) && $token[0] === T_NAMESPACE)
                            $gettingNamespace = true;
                        if(\is_array($token) && $token[0] === T_CLASS)
                            $gettingClass = true;
                        if($gettingNamespace === true)
                            if(\is_array($token) && \in_array($token[0], [T_STRING, T_NS_SEPARATOR]))
                                $namespace .= $token[1];
                            else if($token === ';')
                                $gettingNamespace = false;
                        if($gettingClass === true)
                            if(\is_array($token) && $token[0] === T_STRING) {
                                $class = $token[1];
                                break;
                            }
                    }

                    $className = $namespace ? $namespace . '\\' . $class : $class;
                    if(\preg_match('/Command$/', $className) > 0) {
                        // make sure file with class is loaded
                        require_once $child->getRealPath();
                        /** @var Command $command */
                        $command = new $className;
                        if($prefix !== null)
                            $command->setName($prefix . ':' . $command->getName());
                        $this['console']->add($command);
                    }
                }
        } catch(\UnexpectedValueException $ex) {
            // do nothing - no commands to load
        }
    }
}


<?php
/**
 * Created by PhpStorm.
 * User: sicmouse
 * Date: 2018/9/1
 * Time: 下午7:08
 */

namespace DdvPhp\DdvRestfulApi\Middleware\Laravel;

use Closure;
use Illuminate\Http\Request;
use DdvPhp\DdvRestfulApi\Lib\Auth;
use DdvPhp\DdvRestfulApi\Middleware\Laravel\RequestInfo;
use DdvPhp\DdvRestfulApi\Lib\RequestSignInfo;
use DdvPhp\DdvRestfulApi\Interfaces\RequestInfo as RequestInfoInterface;
use DdvPhp\DdvRestfulApi\AuthData\AuthDataSessionLaravelDriver;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Session\SessionManager;
use Illuminate\Contracts\Session\Session;
use Illuminate\Session\CookieSessionHandler;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Response;

class AuthSession
{

    /**
     * 会话管理对象
     *
     * @var \Illuminate\Session\SessionManager
     */
    protected $manager;

    /**
     * 创建一个新的会话中间件。
     *
     * @param  \Illuminate\Session\SessionManager  $manager
     * @return void
     */
    public function __construct(SessionManager $manager)
    {
        $this->manager = $manager;
    }

    /**
     * 处理传入的请求。
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if (empty($request->ddvHttpRequestInfo) || (!($request->ddvHttpRequestInfo instanceof RequestInfoInterface))) {
            RequestInfo::createHttpRequestInfo($request);
        }
        /**
         * @var RequestSignInfo $requestSignInfo
         */
        $requestSignInfo = new RequestSignInfo();
        /**
         * 设置请求信息
         */
        $requestSignInfo->createRequestInfo($request->ddvHttpRequestInfo);
        $requestSignInfo->setHeadersPrefix(config('ddvRestfulApi.headersPrefix'));

        $auth = new Auth();
        $auth->setRequestSignInfo($requestSignInfo);
        $auth->setConfig(array(
                'authDataDriver' => AuthDataSessionLaravelDriver::class,
                'authDataDriverConfig' => array(
                    'session' => $this
                )
            )
        );
        $auth->sign();

        // 开启会话

        // 如果已配置会话驱动程序，我们需要在此处启动会话
        // 以便数据为应用程序做好准备。 请注意Laravel会话
        // 不要以任何方式使用PHP“本机”会话，因为它们很糟糕。
        if ($this->sessionConfigured()) {
            $request->setLaravelSession(
                $session = $this->startSession($request, $auth->getAccessKeyId())
            );

            $this->collectGarbage($session);
        }

        // 开启项目
        $response = $next($request);
        // 保存会话
        $session->save();

        return $response;
    }

    /**
     * 执行请求生命周期的任何最终操作。
     * Perform any final actions for the request lifecycle.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Symfony\Component\HttpFoundation\Response  $response
     * @return void
     */
    public function terminate($request, $response)
    {
        if ($this->sessionConfigured()){
            $session = $request->getSession();
            if (!empty($session) && method_exists($session, 'save')){
                $session->save();
            }
            unset($session);
        }
    }

    /**
     * Start the session for the given request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Contracts\Session\Session
     */
    protected function startSession(Request $request, $sessionId)
    {
        return tap($this->getSession($sessionId), function ($session) use ($request) {
            $session->setRequestOnHandler($request);
            $session->start();
        });
    }

    /**
     * Get the session implementation from the manager.
     *
     * @param  string  $sessionId
     * @return \Illuminate\Contracts\Session\Session
     */
    public function getSession($sessionId)
    {
        return tap($this->manager->driver(), function ($session) use ($sessionId) {
            $session->setId($sessionId);
        });
    }

    /**
     * 如有必要，从会话中删除垃圾。
     *
     * @param  \Illuminate\Contracts\Session\Session  $session
     * @return void
     */
    protected function collectGarbage(Session $session)
    {
        $config = $this->manager->getSessionConfig();

        // 在这里，我们将通过命中在任何给定请求上执行垃圾收集所需的几率来查看此请求是否达到垃圾收集抽奖。
        // 如果我们点击它，
        // 我们将调用此处理程序让它删除所有过期的会话。
        if ($this->configHitsLottery($config)) {
            $session->getHandler()->gc($this->getSessionLifetimeInSeconds());
        }
    }

    /**
     * Determine if the configuration odds hit the lottery.
     * 确定配置赔率是否达到了彩票。
     *
     * @param  array  $config
     * @return bool
     */
    protected function configHitsLottery(array $config)
    {
        return random_int(1, $config['lottery'][1]) <= $config['lottery'][0];
    }

    /**
     * Get the session lifetime in seconds.
     * 以秒为单位获取会话生存期。
     *
     * @return int
     */
    protected function getSessionLifetimeInSeconds()
    {
        return Arr::get($this->manager->getSessionConfig(), 'lifetime') * 60;
    }

    /**
     * 确定是否已配置会话驱动程序。
     * Determine if a session driver has been configured.
     *
     * @return bool
     */
    protected function sessionConfigured()
    {
        return ! is_null(Arr::get($this->manager->getSessionConfig(), 'driver'));
    }

    /**
     * 确定配置的会话驱动程序是否持久。
     * Determine if the configured session driver is persistent.
     *
     * @param  array|null  $config
     * @return bool
     */
    protected function sessionIsPersistent(array $config = null)
    {
        $config = $config ?: $this->manager->getSessionConfig();

        return ! in_array($config['driver'], [null, 'array']);
    }
}


<?php
/*
This is part of Wedeto, the WEb DEvelopment TOolkit.
It is published under the MIT Open Source License.

Copyright 2017, Egbert van der Wal

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

namespace Wedeto\Application\CLI;

use Wedeto\Util\Dictionary;

/**
 * Provides functions for interacting with a GUI
 */
class CLI
{
    const MAX_LINE_LENGTH = 80;

    private $parameters = array();

    /**
     * Create the CLI object, parsing command line arguments.
     * @throws CLIException When the script is not run from the command line
     */
    public function __construct()
    {
        // Make sure we're running on CLI
        $this->checkCLI();

        // Add the default options
        $this
            ->addOption("l", "loglevel", "level", "Sets the log level for the output")
            ->addOption("f", "logfile", "filename", "Sets the log file where output will be written to")
            ->addOption("s", "silent", false, "If this is set, output is only written to logfile, not to stdout")
            ->addOption("h", "help", false, "Show this help");
    }

    /**
     * @codeCoverageIgnore Tests are always run using CLI SAPI
     */
    private function checkCLI()
    {
        if (PHP_SAPI !== "cli" || !isset($_SERVER['argv']))
            throw new CLIException("Not running on command line");
    }

    public function clearOptions()
    {
        $this->parameters = array();
    }

    /**
     * Register a valid option
     * @param $short string The single character version of this option
     * @param $long string The long form of this option
     * @param $arg string The name/type of parameter, shown in the help text. Set to false to disable the argument
     * @param $description string The description of this option, shown in the help text.
     */
    public function addOption($short, $long, $arg, $description)
    {
        $this->parameters[] = array(
            $short,
            $long,
            $arg,
            $description
        );
        return $this;
    }

    public function getOptString()
    {
        $opt_str = "";
        $long_opts = array();
        $mapping = array();

        foreach ($this->parameters as $param)
        {
            $short = $param[0];
            $long = $param[1];
            $arg = $param[2];

            if ($short)
            {
                if ($long)
                    $mapping[$short] = $long;

                if ($arg)
                    $short .= ":";

                $opt_str .= $short;
            }

            if ($long)
            {
                if ($arg)
                    $long .= ":";
                $long_opts[] = $long;
            }
        }
        return array($opt_str, $long_opts, $mapping);
    }

    /**
     * Parse the provided arguments and options.
     *
     * @param $arguments array The list of arguments to parse
     * @return array Associative array of option => argument pairs. For option without an argument, the argument === false
     * @codeCoverageIgnore We cannot fool getopt to modify the arguments, so this cannot be unit tested
     */
    public function parse()
    {
        list($opt_str, $long_opts, $mapping) = $this->getOptString();
        $opts = \getopt($opt_str, $long_opts);
        $options = $this->mapOptions($opts, $mapping);
        return new Dictionary($options);
    }

    public function mapOptions(array $opts, array $mapping)
    {
        // Map short options to the long options where available
        $res = array();
        foreach ($opts as $k => $v)
        {
            if (isset($mapping[$k]))
                $res[$mapping[$k]] = $v;
            else
                $res[$k] = $v;
        }
        return $res;
    }

    /**
     * Read from standard input
     * @param $prompt string The text explaining what input is expected, null to avoid prompt
     * @param $default string What to return in case the user enters nothing. If not specified,
     *                        this function keeps asking until input is provided
     * @return string The input or default if no input was specified
     * @codeCoverageIgnore STDIN can't be reliably unit tested - test manually
     */
    public static function input($prompt, $default = null)
    {
        $ret = false;
        while (!$ret)
        {
            if ($prompt)
            {
                echo $prompt;
                if ($default)
                    echo " (" . $default . ")";

                echo ": ";
            }
            $ret = trim(fgets(STDIN));

            if (!$ret && $default !== null)
                return $default;
        }

        return trim($ret);
    }

    public static function formatText($indent, $max_width, $text, $ostr = STDOUT)
    {
        $parts = explode(" ", $text);
        $str_part = "";
        $first = true;
        foreach ($parts as $p)
        {
            if (strlen($str_part) + ($first ? $indent : 0) + strlen($p) > $max_width && strlen($str_part) != $indent)
            {
                fprintf($ostr, $str_part . "\n");
                $first = false;
                $str_part = str_pad("", $indent);
            }
            $str_part .= $p . " ";
        }
        if (trim($str_part) !== "")
            fprintf($ostr, $str_part . "\n");
    }

    /**
     * Show the help text, generated based on the configured options
     * @codeCoverageIgnore Validate output manually - formatText is unit tested.
     */
    public function syntax($error = "Please specify valid options")
    {
        $ostr = (!$error) ? STDOUT : STDERR;
        if (is_string($error))
            fprintf($ostr, "Error: %s\n", $error);
        fprintf($ostr, "Syntax: php " . $_SERVER['argv'][0] . " <options> <action>\n\n");
        fprintf($ostr, "Options: \n");
        $max_opt_length = 0;
        $max_arg_length = 0;

        // Sort the parameters alphabetically
        $params = $this->parameters;
        usort($params, function ($a, $b) {
            $lo = !empty($a[0]) ? $a[0] : $a[1];
            $ro = !empty($b[0]) ? $b[0] : $b[1];
            return strcmp($lo, $ro);
        });

        foreach ($params as $param)
        {
            $max_opt_length = max(strlen($param[1]) + 3, $max_opt_length);
            $max_arg_length = max(strlen($param[2]) + 3, $max_arg_length);
        }

        foreach ($this->parameters as $param)
        {
            fprintf($ostr, "    ");
            $so = $param[0] ? "-" . $param[0] : "";
            $lo = $param[1] ? "--" . $param[1] : "";
            $arg = $param[2] ? '<' . $param[2] . '>' : "";

            $pstr = sprintf("%-2s %-" . $max_opt_length . "s %-" . $max_arg_length . "s ", $so, $lo, $arg);
            $indent = strlen($pstr) + 4;
            fprintf($ostr, $pstr);

            self::formatText($indent, self::MAX_LINE_LENGTH, $param[3], $ostr);
        }
        exit($error === false ? 0 : 1);
    }
}


<?php

/**
 * File object
 *
 * @package    RpkUtils\Os
 * @author     Repkit <repkit@gmail.com>
 * @copyright  2015 Repkit
 * @license    MIT <http://opensource.org/licenses/MIT>
 * @since      2015-11-12
 */

 namespace RpkUtils\Os;

 class File
 {
     /**
      * Create file
      * @param string $dir full path and file name
      *         example /home/coco/readme.md
      * @return bool
     */
    public static function create($dir)
    {
        //create dir structure if necesary
        $parts = explode(DIRECTORY_SEPARATOR, $dir);
        $file = array_pop($parts);
        $dir = '';
        foreach($parts as $part)
            if(!is_dir($dir .= DIRECTORY_SEPARATOR.$part)) mkdir($dir);

        //create file
        $handle = fopen($dir.DIRECTORY_SEPARATOR.$file, 'cb');
        fclose($handle);

        return true;
    }
 }


<?php

namespace Data\Type;

use InvalidArgumentException;

class IntType extends FloatType
{
    /**
     * Check the value
     *
     * @param  mixed $value
     * @return int
     */
    protected function check($value)
    {
        if (is_int($value)) {
            return $value;
        }

        if ($value === false || $value === 0.0 || $value === '0') {
            return 0;
        }

        if ($value === true || $value === 1.0 || $value === '1') {
            return 1;
        }

        if ($value instanceof IntType) {
            return $value->value();
        }

        if ($value instanceof Type) {
            $value = $value->value();
        } else {
            if (is_array($value)) {
                throw new InvalidArgumentException('Invalid int, array given');
            }

            if (is_resource($value)) {
                throw new InvalidArgumentException('Invalid int, resource given');
            }

            if (is_object($value)) {
                throw new InvalidArgumentException('Invalid int, object given');
            }
        }

        try {
            $value = parent::check($value);
        } catch (InvalidArgumentException $e) {
            throw new InvalidArgumentException('Invalid int: ' . $value);
        }

        if (filter_var($value, FILTER_VALIDATE_INT) === false) {
            throw new InvalidArgumentException('Invalid int: ' . $value);
        }

        return (int) $value;
    }

    /**
     * Is it even?
     *
     * @return boolean
     */
    public function isEven()
    {
        if ($this->value % 2 === 0) {
            return true;
        }

        return false;
    }

    /**
     * Is it odd?
     *
     * @return boolean
     */
    public function isOdd()
    {
        if ($this->value % 2 !== 0) {
            return true;
        }

        return false;
    }

    /**
     * Is this a prime?
     *
     * @return boolean
     */
    public function isPrime()
    {
        if ($this->value < 2) {
            return false;
        }

        if ($this->value === 2) {
            return true;
        }

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

        for ($i = 3; $i <= ceil(sqrt($this->value)); $i = $i + 2) {
            if ($this->value % $i == 0) {
                return false;
            }
        }

        return true;
    }
}


<?php
/**
 * Phossa Project
 *
 * PHP version 5.4
 *
 * @category  Library
 * @package   Phossa\Query
 * @author    Hong Zhang <phossa@126.com>
 * @copyright 2015 phossa.com
 * @license   http://mit-license.org/ MIT License
 * @link      http://www.phossa.com/
 */
/*# declare(strict_types=1); */

namespace Phossa\Query\Statement;

use Phossa\Query\Clause\OnTrait;
use Phossa\Query\Clause\WhereTrait;

/**
 * Statement part for WHERE/ON etc.
 *
 * @package Phossa\Query
 * @author  Hong Zhang <phossa@126.com>
 * @see     ExpressionInterface
 * @version 1.0.0
 * @since   1.0.0 added
 */
class Expression extends StatementAbstract implements ExpressionInterface
{
    use WhereTrait, OnTrait;

    /**
     * configs
     *
     * @var    array
     * @access protected
     */
    protected $config = [
        // on
        'on' => [
            'prefix'    => '',
            'func'      => 'buildOn',
            'join'      => ' ',
        ],
        // where
        'where' => [
            'prefix'    => '',
            'func'      => 'buildWhere',
            'join'      => ' ',
        ],
    ];

    /**
     * {@inheritDoc}
     */
    protected function build()/*# : string */
    {
        $result = [];

        foreach ($this->getConfig() as $part) {
            $built = call_user_func([$this, $part['func']]);
            if (!empty($built)) {
                $result[] =
                    $part['prefix'] . (empty($part['prefix']) ? '' : ' ') .
                    join($part['join'], $built);
            }
        }
        return '(' . join($this->settings['seperator'], $result) . ')';
    }
}


<?php namespace Coderjp\Verifier;
/**
 * This file is part of Verifier.
 *
 * @license MIT
 * @package Coderjp\Verifier
 */

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Config;

class MigrationCommand extends Command
{
    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'verifier:migration';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Creates a migration for Verifier';

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function fire()
    {

        $this->laravel->view->addNamespace('verifier', substr(__DIR__, 0, -8).'views');

        $tables = Config::get('verifier.tables');

        $this->line('');
        $this->info( 'Tables: '. implode(',', $tables) );

        $msg = 'A migration that adds Verifier columns to '. implode(',', $tables) .' will be created in '.
            'database/migrations directory';

        $this->comment($msg);
        $this->line('');

        if ($this->confirm("Proceed with the migration creation? [Yes|no]", "Yes")) {

            $this->line('');
            $this->info("Creating migration...");

            if ($this->createMigration($tables)) {

                $this->info("Migration successfully created!");

            } else {

                $this->error(
                    "Couldn't create migration.\n Check the write permissions".
                    " within the database/migrations directory."
                );

            }

            $this->line('');
        }
    }

    /**
     * Create the migration.
     *
     * @param array $tables
     *
     * @return bool
     */
    protected function createMigration($tables)
    {
        $codeColumn = Config::get('verifier.store_column');
        $flagColumn = Config::get('verifier.flag_column');

        $migrationFile = base_path("/database/migrations")."/".date('Y_m_d_His')."_verifier_add_columns.php";

        $data = compact('codeColumn', 'flagColumn', 'tables');

        $output = $this->laravel->view->make('verifier::generators.migration')->with($data)->render();

        if (!file_exists($migrationFile) && $fs = fopen($migrationFile, 'x')) {

            fwrite($fs, $output);
            fclose($fs);
            return true;

        }

        return false;

    }
}


<?php
/*
 * WellCommerce Open-Source E-Commerce Platform
 *
 * This file is part of the WellCommerce package.
 *
 * (c) Adam Piotrowski <adam@wellcommerce.org>
 *
 * For the full copyright and license information,
 * please view the LICENSE file that was distributed with this source code.
 */

namespace WellCommerce\Bundle\ProductBundle\Helper;

use WellCommerce\Bundle\CurrencyBundle\Helper\CurrencyHelperInterface;
use WellCommerce\Bundle\ProductBundle\Entity\ProductInterface;
use WellCommerce\Bundle\ProductBundle\Entity\VariantInterface;
use WellCommerce\Bundle\ProductBundle\Entity\VariantOptionInterface;

/**
 * Class VariantHelper
 *
 * @author  Adam Piotrowski <adam@wellcommerce.org>
 */
class VariantHelper implements VariantHelperInterface
{
    /**
     * @var CurrencyHelperInterface
     */
    protected $currencyHelper;

    /**
     * VariantHelper constructor.
     *
     * @param CurrencyHelperInterface $currencyHelper
     */
    public function __construct(CurrencyHelperInterface $currencyHelper)
    {
        $this->currencyHelper = $currencyHelper;
    }

    /**
     * {@inheritdoc}
     */
    public function getVariants(ProductInterface $product) : array
    {
        $variants = [];

        $product->getVariants()->map(function (VariantInterface $variant) use (&$variants) {
            if ($variant->isEnabled()) {
                $this->extractVariantData($variant, $variants);
            }
        });

        return $variants;
    }

    /**
     * {@inheritdoc}
     */
    public function getAttributes(ProductInterface $product) : array
    {
        $attributes = [];

        $product->getVariants()->map(function (VariantInterface $variant) use (&$attributes) {
            if ($variant->isEnabled()) {
                $this->extractAttributesData($variant, $attributes);
            }
        });

        return $attributes;
    }

    protected function extractAttributesData(VariantInterface $variant, &$attributes)
    {
        $variant->getOptions()->map(function (VariantOptionInterface $variantOption) use (&$attributes) {
            $attribute                                                           = $variantOption->getAttribute();
            $attributeValue                                                      = $variantOption->getAttributeValue();
            $attributes[$attribute->getId()]['name']                             = $attribute->translate()->getName();
            $attributes[$attribute->getId()]['values'][$attributeValue->getId()] = $attributeValue->translate()->getName();
        });
    }

    protected function extractVariantData(VariantInterface $variant, array &$variants)
    {
        $sellPrice    = $variant->getSellPrice();
        $baseCurrency = $sellPrice->getCurrency();
        $key          = $this->getVariantOptionsKey($variant);

        $variants[$key] = [
            'id'                 => $variant->getId(),
            'finalPriceGross'    => $this->currencyHelper->convertAndFormat($sellPrice->getFinalGrossAmount(), $baseCurrency),
            'sellPriceGross'     => $this->currencyHelper->convertAndFormat($sellPrice->getGrossAmount(), $baseCurrency),
            'discountPriceGross' => $this->currencyHelper->convertAndFormat($sellPrice->getDiscountedGrossAmount(), $baseCurrency),
            'stock'              => $variant->getStock(),
            'symbol'             => $variant->getSymbol(),
            'options'            => $this->getVariantOptions($variant)
        ];
    }

    protected function getVariantOptionsKey(VariantInterface $variant) : string
    {
        $options = [];

        $variant->getOptions()->map(function (VariantOptionInterface $variantOption) use (&$options) {
            $attribute      = $variantOption->getAttribute();
            $attributeValue = $variantOption->getAttributeValue();
            $key            = sprintf('%s:%s', $attribute->getId(), $attributeValue->getId());
            $options[$key]  = $key;
        });

        ksort($options);

        return implode(',', $options);
    }

    public function getVariantOptions(VariantInterface $variant) : array
    {
        $options = [];

        $variant->getOptions()->map(function (VariantOptionInterface $variantOption) use (&$options) {
            $attribute                                   = $variantOption->getAttribute();
            $attributeValue                              = $variantOption->getAttributeValue();
            $options[$attribute->translate()->getName()] = $attributeValue->translate()->getName();
        });

        return $options;
    }
}


<?php

declare(strict_types=1);

namespace Wtf\ORM;

use Exception;
use Respect\Validation\Exceptions\NestedValidationException;
use Slim\Collection;

abstract class Entity extends \Wtf\Root
{
    protected $relationObjects = [];
    protected $scheme;

    /**
     * Get short entity name (without namespace)
     * Helper function, required for lazy load.
     *
     * @return string
     */
    protected function __getEntityName(): string
    {
        return ($pos = \strrpos(\get_class($this), '\\')) ? \substr(\get_class($this), $pos + 1) : \get_class($this);
    }

    /**
     * Magic relation getter.
     *
     * @param null|string $method
     * @param array       $params
     */
    public function __call(?string $method = null, array $params = [])
    {
        $parts = \preg_split('/([A-Z][^A-Z]*)/', $method, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
        $type = \array_shift($parts);
        $relation = \strtolower(\implode('_', $parts));

        if ('get' === $type && isset($this->getRelations()[$relation])) {
            return $this->loadRelation($relation);
        }

        return parent::__call($method, $params);
    }

    /**
     * Get entity scheme.
     *
     * @return array
     */
    public function getScheme(): array
    {
        if (null === $this->scheme) {
            $raw = $this->medoo->query('DESCRIBE '.$this->getTable())->fetchAll();
            $this->scheme = [];
            foreach ($raw as $field) {
                $this->scheme[$field['Field']] = $field;
            }
        }

        return $this->scheme;
    }

    /**
     * Save entity data in db.
     *
     * @param bool $validate
     *
     * @throws Exception if entity data is not valid
     *
     * @return Entity
     */
    public function save(bool $validate = true): self
    {
        if ($validate && $this->validate()) {
            throw new Exception('Entity '.$this->__getEntityName().' data is not valid');
        }

        /**
         * Remove fields that not exists in DB table scheme,
         * to avoid thrown exceptions on saving garbadge fields.
         */
        $scheme = \array_keys($this->getScheme());
        foreach ($this->data as $key => $value) {
            if (!\in_array($key, $scheme, true)) {
                unset($this->data[$key]);
            }
        }

        if ($this->getId()) {
            $this->medoo->update($this->getTable(), $this->data, ['id' => $this->getId()]);
        } else {
            $this->medoo->insert($this->getTable(), $this->data);
            $this->setId($this->medoo->id());
        }
        $this->sentry->breadcrumbs->record([
            'message' => 'Entity '.$this->__getEntityName().'::save()',
            'data' => ['query' => $this->medoo->last()],
            'category' => 'Database',
            'level' => 'info',
        ]);

        return $this;
    }

    /**
     * Validate entity data.
     *
     * @param string $method Validation for method, default: save
     *
     * @return array [['field' => 'error message']]
     */
    public function validate(string $method = 'save'): array
    {
        $errors = [];
        foreach ($this->getValidators()[$method] ?? [] as $field => $validator) {
            try {
                $validator->setName($field)->assert($this->get($field));
            } catch (NestedValidationException $e) {
                $errors[$field] = $e->getMessages();
            }
        }

        return $errors;
    }

    /**
     * Load entity (data from db).
     *
     * @param mixed  $value  Field value (eg: id field with value = 10)
     * @param string $field  Field name, default: id
     * @param array  $fields Fields (columns) to load, default: all
     *
     * @return Entity
     */
    public function load($value, $field = 'id', array $fields = null): self
    {
        $data = $this->medoo->get($this->getTable(), $fields ?? '*', [$field => $value]);
        $this->data = \is_array($data) ? $data : []; //handle empty result gracefuly
        $this->sentry->breadcrumbs->record([
            'message' => 'Entity '.$this->__getEntityName().'::load('.$value.', '.$field.', ['.\implode(', ', $fields ?? []).')',
            'data' => ['query' => $this->medoo->last()],
            'category' => 'Database',
            'level' => 'info',
        ]);

        return $this;
    }

    /**
     * Get all entities from db.
     *
     * @param array $where  Where clause
     * @param bool  $assoc  Return collection of entity objects OR of assoc arrays
     * @param array $fields Fields to load, default is all
     *
     * @return Collection
     */
    public function loadAll(array $where = [], bool $assoc = false, array $fields = null): Collection
    {
        $allData = $this->medoo->select($this->getTable(), $fields ? $fields : '*', $where);
        $this->sentry->breadcrumbs->record([
            'message' => 'Entity '.$this->__getEntityName().'::loadAll('.\print_r($where, true).', '.$assoc.', '.\print_r($fields, true).')',
            'data' => ['query' => $this->medoo->last()],
            'category' => 'Database',
            'level' => 'info',
        ]);
        $items = [];
        foreach ($allData as $data) {
            $items[] = ($assoc) ? $data : $this->container['entity']($this->__getEntityName())->setData($data);
        }

        return new Collection($items);
    }

    /**
     * Load realated entity by relation name.
     *
     * @param string $name Relation name
     *
     * @return null|Collection|Entity
     */
    public function loadRelation(string $name)
    {
        if (!isset($this->relationObjects[$name]) || empty($this->relationObjects[$name])) {
            $relation = $this->getRelations()[$name];
            if (!$relation || !$relation['entity'] || !$this->get($relation['key'] ?? 'id')) {
                return null;
            }

            $entity = $this->entity($relation['entity']);
            $type = $relation['type'] ?? 'has_one';
            $key = $relation['key'] ?? ('has_one' === $type ? $this->__getEntityName().'_id' : 'id');
            $foreignKey = $relation['foreign_key'] ?? ('has_one' === $type ? 'id' : $this->__getEntityName().'_id');
            $assoc = $relation['assoc'] ?? false;
            $this->relationObjects[$name] = ('has_one' === $type) ? $entity->load($this->get($key), $foreignKey) : $entity->loadAll([$foreignKey => $this->get($key)], $assoc);
        }

        return $this->relationObjects[$name] ?? null;
    }

    /**
     * Determine whether the target data existed.
     *
     * @param array $where
     *
     * @return bool
     */
    public function has(array $where = []): bool
    {
        return $this->medoo->has($this->getTable(), $where);
    }

    /**
     * Get count of items by $where conditions.
     *
     * @param array $where Where clause
     *
     * @return int
     */
    public function count(array $where = []): int
    {
        return $this->medoo->count($this->getTable(), $where);
    }

    /**
     * Delete entity row from db.
     *
     * @return bool
     */
    public function delete(): bool
    {
        return (bool) $this->medoo->delete($this->getTable(), ['id' => $this->getId()]);
    }

    /**
     * Return entity table name.
     *
     * @return string
     */
    abstract public function getTable(): string;

    /**
     * Get list of field validations
     * Structure:
     *
     * [
     *     '<method>' => [
     *         '<field_name>' => v::stringType()->length(1, 255),
     *          //...
     *     ],
     * ];
     *
     * Example: ['save' => ['name' => v::stringType()->length(1,255)]].
     *
     * @return array
     */
    abstract public function getValidators(): array;

    /**
     * Return array of entity relations
     *
     * //structure
     * [
     *     'relation__name' => [
     *         'entity' => 'another_entity_name',
     *         'type' => 'has_one', //default, other options: has_many
     *         'key' => 'current_entity_key', //optional, default for has_one: <current_entity>_id, for has_many: id
     *         'foreign_key' => 'another_entity_key', //optional, default for has_one: id, for has_many: '<current_entity>_id'
     *         'assoc' => false, //optional, return data arrays instead of objects on "has_many", default: false
     *      ],
     * ];.
     *
     * //Example (current entity: blog post, another entity: user)
     * [
     *     'author' => [ //has_one
     *         'entity' => 'user',
     *         'key' => 'author_id',
     *         'foreign_key' => 'id'
     *     ],
     * ];
     * //Example (same as above, but with default values)
     * [
     *     'author' => [
     *         'entity' => 'user',
     *     ],
     * ];
     * //This example can be called like $blogPostEntity->getAuthor()
     *
     * //Example (current entity: user, another entity: blog post)
     * [
     *     'posts' => [
     *         'entity' => 'post',
     *         'type' => 'has_many',
     *         'foreign_key' => 'author_id',
     *     ],
     * ]
     * //This example can be called like $userEntity->getPosts()
     *
     *
     * @return array
     */
    abstract public function getRelations(): array;
}


<?php declare(strict_types=1);

namespace Identity\Events;

use GeneralForm\IEvent;
use GeneralForm\IEventContainer;
use Identity\IIdentityModel;
use Nette\SmartObject;

/**
 * Class ApproveLinkEvent
 *
 * @author  geniv
 * @package Identity\Events
 */
class ApproveLinkEvent implements IEvent
{
    use SmartObject;

    /** @var string */
    private $validate, $destination;
    /** @var IIdentityModel */
    private $identityModel;

    /**
     * ApproveLinkEvent constructor.
     *
     * @param string         $validate
     * @param string         $destination
     * @param IIdentityModel $identityModel
     */
    public function __construct(string $validate, string $destination, IIdentityModel $identityModel)
    {
        $this->setValidate($validate);
        $this->setDestination($destination);
        $this->identityModel = $identityModel;
    }

    /**
     * Set validate.
     *
     * @param string $validate
     */
    public function setValidate(string $validate)
    {
        $this->validate = $validate;
    }

    /**
     * Set destination.
     *
     * @param string $destination
     */
    public function setDestination(string $destination)
    {
        $this->destination = $destination;
    }

    /**
     * Update.
     *
     * @param IEventContainer $eventContainer
     * @param array           $values
     */
    public function update(IEventContainer $eventContainer, array $values)
    {
        if (isset($values['id_user'])) {
            $hash = $this->identityModel->getEncodeHash($values['id_user'], $values['login'], $this->validate);

            $component = $eventContainer->getComponent();
            $eventContainer->addValues(['approve_link' => $component->presenter->link($this->destination, $hash)]);
        }
    }
}