<?php

namespace Ease\Html;

/**
 * Common HTML tag class.
 *
 * @author     Vitex <vitex@hippy.cz>
 */
class Tag extends \Ease\Page
{
    /**
     * Jméno tagu - je použit i jako jméno objektu.
     *
     * @var string
     */
    public $tagName = null;

    /**
     * Typ tagu - např A či STRONG.
     *
     * @var string
     */
    public $tagType = null;

    /**
     * Pole vlastností tagu.
     *
     * @var array
     */
    public $tagProperties = null;

    /**
     * pole ze kterého se rendruje obsah STYLE tagu.
     *
     * @var array
     */
    public $cssProperties = null;

    /**
     * Nelogovat události HTML objektů.
     *
     * @var string
     */
    public $logType = 'none';

    /**
     * Koncové lomítko pro xhtml.
     *
     * @var string
     */
    public $trail = ' /';

    /**
     * Má si objekt automaticky plnit vlastnost name ?
     */
    public $setName = false;

    /**
     * Objekt pro vykreslení obecného nepárového html tagu.
     *
     * @param string       $tagType       typ tagu
     * @param array|string $tagProperties parametry tagu
     */
    public function __construct($tagType = null, $tagProperties = null)
    {
        if (is_null($tagType)) {
            $tagType = $this->tagType;
        } else {
            $this->setTagType($tagType);
        }
        parent::__construct();
        if ($tagProperties) {
            $this->setTagProperties($tagProperties);
        }
    }

    /**
     * Set ObjectName
     * Nastaví jméno objektu.
     *
     * @param string $objectName jméno objektu
     *
     * @return string New object name
     */
    public function setObjectName($objectName = null)
    {
        if (!is_null($objectName)) {
            return parent::setObjectName($objectName);
        }
        if (!empty($this->tagName)) {
            return parent::setObjectName(get_class($this).'@'.$this->tagName);
        } else {
            if (!empty($this->tagType)) {
                return parent::setObjectName(get_class($this).'@'.$this->tagType);
            } else {
                return parent::setObjectName();
            }
        }
    }

    /**
     * Nastaví jméno tagu. Unused ...
     *
     * @param string $tagName jméno tagu do vlastnosti NAME
     */
    public function setTagName($tagName)
    {
        $this->tagName = $tagName;
        if ($this->setName) {
            $this->tagProperties['name'] = $tagName;
        }
        $this->setObjectName();
    }

    /**
     * Returns name of tag.
     *
     * @return string
     */
    public function getTagName()
    {
        $tagName = null;
        if ($this->setName === true) {
            if (isset($this->tagProperties['name'])) {
                $tagName = $this->tagProperties['name'];
            }
        } else {
            $tagName = $this->tagName;
        }

        return $tagName;
    }

    /**
     * Nastaví typ tagu.
     *
     * @param string $tagType typ tagu - např. img
     */
    public function setTagType($tagType)
    {
        $this->tagType = $tagType;
    }

    /**
     * Vrací typ tagu.
     *
     * @return string typ tagu - např. img
     */
    public function getTagType()
    {
        return $this->tagType;
    }

    /**
     * Nastaví classu tagu.
     *
     * @param string $className jméno css třídy
     */
    public function setTagClass($className)
    {
        $this->setTagProperties(['class' => $className]);
    }

    /**
     * Přidá classu tagu.
     *
     * @param string $className jméno css třídy
     */
    public function addTagClass($className)
    {
        $this->setTagClass(trim($this->getTagClass().' '.$className));
    }

    /**
     * Vrací css classu tagu.
     */
    public function getTagClass()
    {
        return $this->getTagProperty('class');
    }

    /**
     * Nastaví tagu zadane id, nebo vygenerované náhodné.
     *
     * @param string $tagID #ID html tagu pro JavaScript a Css
     *
     * @return string nastavené ID
     */
    public function setTagID($tagID = null)
    {
        if (is_null($tagID)) {
            $this->setTagProperties(['id' => \Ease\Brick::randomString()]);
        } else {
            $this->setTagProperties(['id' => $tagID]);
        }

        return $this->getTagID();
    }

    /**
     * Vrací ID html tagu.
     *
     * @return string
     */
    public function getTagID()
    {
        return $this->getTagProperty('id');
    }

    /**
     * Returns property tag value.
     *
     * @param string $propertyName název vlastnosti tagu. např. "src" u obrázku
     *
     * @return string current tag property value
     */
    public function getTagProperty($propertyName)
    {
        $property = null;
        if (isset($this->tagProperties[$propertyName])) {
            $property = $this->tagProperties[$propertyName];
        }

        return $property;
    }

    /**
     * Nastaví paramatry tagu.
     *
     * @param mixed $tagProperties asociativní pole parametrů tagu
     */
    public function setTagProperties(array $tagProperties)
    {
        if (isset($tagProperties['id'])) {
            $tagProperties['id'] = preg_replace('/[^A-Za-z0-9_\\-]/', '',
                $tagProperties['id']);
        }
        if (is_array($this->tagProperties)) {
            $this->tagProperties = array_merge($this->tagProperties,
                $tagProperties);
        } else {
            $this->tagProperties = $tagProperties;
        }
        if (isset($tagProperties['name'])) {
            $this->setTagName($tagProperties['name']);
        }
    }

    /**
     * Vrátí parametry tagu jako řetězec.
     *
     * @param mixed $tagProperties asociativní pole parametrú nebo řetězec
     *
     * @return string
     */
    public function tagPropertiesToString($tagProperties = null)
    {
        $tagPropertiesString = '';
        if (!$tagProperties) {
            $tagProperties = $this->tagProperties;
        }
        if (is_array($tagProperties)) {
            $tagPropertiesString = ' ';
            foreach ($tagProperties as $tagPropertyName => $tagPropertyValue) {
                if ($tagPropertyName) {
                    if (is_numeric($tagPropertyName)) {
                        if (!strstr($tagPropertiesString,
                                ' '.$tagPropertyValue.' ')) {
                            $tagPropertiesString .= ' '.$tagPropertyValue.' ';
                        }
                    } else {
                        $tagPropertiesString .= $tagPropertyName.'="'.$tagPropertyValue.'" ';
                    }
                } else {
                    $tagPropertiesString .= $tagPropertyValue.' ';
                }
            }

            $tagPropertiesString = trim($tagPropertiesString);
        }
        return $tagPropertiesString;
    }

    /**
     * Nastaví paramatry Css.
     *
     * @param array $cssProperties asociativní pole, nebo CSS definice
     */
    public function setTagCss(array $cssProperties)
    {
        $this->cssProperties = $cssProperties;
        $this->setTagProperties(['style' => $this->cssPropertiesToString()]);
    }

    /**
     * Vrátí parametry Cssu jako řetězec.
     *
     * @param array|string $cssProperties pole vlastností nebo CSS definice
     *
     * @return string
     */
    public function cssPropertiesToString($cssProperties = null)
    {
        $cssPropertiesString = '';
        if (!$cssProperties) {
            $cssProperties = $this->cssProperties;
        }
        $cssPropertiesString = ' ';
        foreach ($cssProperties as $CssPropertyName => $CssPropertyValue) {
            $cssPropertiesString .= $CssPropertyName.':'.$CssPropertyValue.';';
        }
        return trim($cssPropertiesString);
    }

    /**
     * Add Css to tag properties.
     */
    public function finalize()
    {
        if (!empty($this->cssProperties)) {
            $this->tagProperties['style'] = $this->cssPropertiesToString();
        }
    }

    /**
     * Vykreslí tag.
     */
    public function draw()
    {
        echo '<' . trim($this->tagType.' '.$this->tagPropertiesToString());
        echo $this->trail.'>';
        $this->drawStatus = true;
    }
}


<?php
/**
 * Třída pro logování.
 *
 * @author    Vitex <vitex@hippy.cz>
 * @copyright 2009-2016 Vitex@hippy.cz (G)
 */

namespace Ease\Logger;

class ToFile extends ToMemory
{
    /**
     * Předvolená metoda logování.
     *
     * @var string
     */
    public $logType = 'file';

    /**
     * Adresář do kterého se zapisují logy.
     *
     * @var string dirpath
     */
    public $logPrefix = null;

    /**
     * Soubor s do kterého se zapisuje log.
     *
     * @var string
     */
    public $logFileName = 'Ease.log';

    /**
     * úroveň logování.
     *
     * @var string - silent,debug
     */
    public $logLevel = 'debug';

    /**
     * Soubor do kterého se zapisuje report.
     *
     * @var string filepath
     */
    public $reportFile = 'EaseReport.log';

    /**
     * Soubor do kterého se lougují pouze zprávy typu Error.
     *
     * @var string filepath
     */
    public $errorLogFile = 'EaseErrors.log';

    /**
     * Odkaz na vlastnící objekt.
     *
     * @var EaseSand ||
     */
    public $parentObject = null;

    /**
     * Filedescriptor Logu.
     *
     * @var resource
     */
    private $_logFileHandle = null;

    /**
     * Filedescriptor chybového Logu.
     *
     * @var resource
     */
    private $_errorLogFileHandle = null;

    /**
     * ID naposledy ulozene zpravy.
     *
     * @var int unsigned
     */
    private $messageID = 0;

    /**
     * Obecné konfigurace frameworku.
     *
     * @var Shared
     */
    public $easeShared = null;

    /**
     * Saves obejct instace (singleton...).
     */
    private static $_instance = null;

    /**
     * Logovací třída.
     *
     * @param string $baseLogDir
     */
    public function __construct($baseLogDir = null)
    {
        $this->easeShared = \Ease\Shared::singleton();
        $this->setupLogFiles($baseLogDir);
    }

    /**
     * Pri vytvareni objektu pomoci funkce singleton (ma stejne parametry, jako
     * konstruktor) se bude v ramci behu programu pouzivat pouze jedna jeho
     * instance (ta prvni).
     *
     * @link http://docs.php.net/en/language.oop5.patterns.html Dokumentace a
     * priklad
     */
    public static function singleton()
    {
        if (!isset(self::$_instance)) {
            $Class           = __CLASS__;
            self::$_instance = new $Class();
        }

        return self::$_instance;
    }

    /**
     * Nastaví cesty logovacích souborů.
     * @param string $baseLogDir
     */
    public function setupLogFiles($baseLogDir = null)
    {
        if (is_null($baseLogDir)) {
            $baseLogDir = $this->logPrefix;
        }

        if (is_null($baseLogDir) && defined('LOG_DIRECTORY')) {
            $baseLogDir = constant('LOG_DIRECTORY');
        }

        if (!empty($baseLogDir)) {
            $this->logPrefix = \Ease\Brick::sysFilename($baseLogDir);
            if ($this->TestDirectory($this->logPrefix)) {
                $this->logFileName  = $this->logPrefix.$this->logFileName;
                $this->reportFile   = $this->logPrefix.$this->reportFile;
                $this->errorLogFile = $this->logPrefix.$this->errorLogFile;
            } else {
                $this->logPrefix    = null;
                $this->logFileName  = null;
                $this->reportFile   = null;
                $this->errorLogFile = null;
            }
        } else {
            $this->logType      = 'none';
            $this->logPrefix    = null;
            $this->logFileName  = null;
            $this->reportFile   = null;
            $this->errorLogFile = null;
        }
    }

    /**
     * Zapise zapravu do logu.
     *
     * @param string $caller  název volajícího objektu
     * @param string $message zpráva
     * @param string $type    typ zprávy (success|info|error|warning|*)
     *
     * @return null|boolean byl report zapsán ?
     */
    public function addToLog($caller, $message, $type = 'message')
    {
        ++$this->messageID;
        if (($this->logLevel == 'silent') && ($type != 'error')) {
            return;
        }
        if (($this->logLevel != 'debug') && ($type == 'debug')) {
            return;
        }
        $this->statusMessages[$type][$this->messageID] = $message;

        $message = htmlspecialchars_decode(strip_tags(stripslashes($message)));

        $logLine = date(DATE_ATOM).' ('.$caller.') '.str_replace(['notice', 'message',
                'debug', 'report', 'error', 'warning', 'success', 'info', 'mail',],
                ['**', '##', '@@', '::'], $type).' '.$message."\n";
        if (!isset($this->logStyles[$type])) {
            $type = 'notice';
        }
        if ($this->logType == 'console' || $this->logType == 'both') {
            if ($this->runType == 'cgi') {
                echo $logLine;
            } else {
                echo '
logStyles[$type].'">'.$logLine."
\n";
                flush();
            }
        }
        if (!empty($this->logPrefix)) {
            if ($this->logType == 'file' || $this->logType == 'both') {
                if (!empty($this->logFileName)) {
                    if (!$this->_logFileHandle) {
                        $this->_logFileHandle = fopen($this->logFileName, 'a+');
                    }
                    if ($this->_logFileHandle !== null) {
                        fwrite($this->_logFileHandle, $logLine);
                    }
                }
            }
            if ($type == 'error') {
                if (!empty($this->errorLogFile)) {
                    if (!$this->_errorLogFileHandle) {
                        $this->_errorLogFileHandle = fopen($this->errorLogFile,
                            'a+');
                    }
                    if ($this->_errorLogFileHandle) {
                        fputs($this->_errorLogFileHandle, $logLine);
                    }
                }
            }
        }

        return true;
    }

    /**
     * Přejmenuje soubor s logem.
     *
     * @param string $newLogFileName new log filename
     *
     * @return string
     */
    public function renameLogFile($newLogFileName)
    {
        $newLogFileName = dirname($this->logFileName).'/'.basename($newLogFileName);
        if (rename($this->logFileName, $newLogFileName)) {
            return realpath($newLogFileName);
        } else {
            return realpath($this->logFileName);
        }
    }

    /**
     * Detekuje a nastaví zdali je objekt suštěn jako script (cgi) nebo jako page (web).
     *
     * @param string $runType force type
     *
     * @return string type
     */
    public function setRunType($runType = null)
    {
        if (!$runType) {
            if (isset($_SERVER['HTTP_HOST'])) {
                $this->runType = 'web';
            } else {
                $this->runType = 'cgi';
            }

            return $this->runType;
        }
        if (($runType != 'web') || ($runType != 'cgi')) {
            return;
        } else {
            return $this->runType;
        }
    }

    /**
     * Zkontroluje stav adresáře a upozorní na případné nesnáze.
     *
     * @param string $directoryPath cesta k adresáři
     * @param bool   $isDir         detekovat existenci adresáře
     * @param bool   $isReadable    testovat čitelnost
     * @param bool   $isWritable    testovat zapisovatelnost
     * @param bool   $logToFile     povolí logování do souboru
     *
     * @return bool konečný výsledek testu
     */
    public static function testDirectory($directoryPath, $isDir = true,
                                         $isReadable = true, $isWritable = true,
                                         $logToFile = false)
    {
        $sanity = true;
        if ($isDir) {
            if (!is_dir($directoryPath)) {
                echo $directoryPath._(' not an folder. Current directory:').' '.getcwd();
                if ($logToFile) {
                    \Ease\Shared::logger()->addToLog('TestDirectory',
                        $directoryPath._(' not an folder. Current directory:').' '.getcwd());
                }
                $sanity = false;
            }
        }
        if ($isReadable) {
            if (!is_readable($directoryPath)) {
                echo $directoryPath._(' not an readable folder. Current directory:').' '.getcwd();
                if ($logToFile) {
                    \Ease\Shared::logger()->addToLog('TestDirectory',
                        $directoryPath._(' not an readable folder. Current directory:').' '.getcwd());
                }
                $sanity = false;
            }
        }
        if ($isWritable) {
            if (!is_writable($directoryPath)) {
                echo $directoryPath._(' not an writeable folder. Current directory:').' '.getcwd();
                if ($logToFile) {
                    \Ease\Shared::logger()->addToLog('TestDirectory',
                        $directoryPath._(' not an writeable folder. Current directory:').' '.getcwd());
                }

                $sanity = false;
            }
        }

        return $sanity;
    }

    /**
     * Oznamuje chybovou událost.
     *
     * @param string $caller     název volající funkce, nebo objektu
     * @param string $message    zpráva
     * @param mixed  $objectData data k zaznamenání
     */
    public function error($caller, $message, $objectData = null)
    {
        if ($this->errorLogFile) {
            $logFileHandle = @fopen($this->errorLogFile, 'a+');
            if ($logFileHandle) {
                if ($this->easeShared->runType == 'web') {
                    fputs($logFileHandle,
                        \Ease\Brick::printPreBasic($_SERVER)."\n #End of Server enviroment  <<<<<<<<<<<<<<<<<<<<<<<<<<< # \n\n");
                } else {
                    fputs($logFileHandle,
                        \Ease\Brick::printPreBasic($_ENV)."\n #End of CLI enviroment  <<<<<<<<<<<<<<<<<<<<<<<<<<< # \n\n");
                }
                if (isset($_POST) && count($_POST)) {
                    fputs($logFileHandle,
                        \Ease\Brick::printPreBasic($_POST)."\n #End of _POST  <<<<<<<<<<<<<<<<<<<<<<<<<<< # \n\n");
                }
                if (isset($_GET) && count($_GET)) {
                    fputs($logFileHandle,
                        \Ease\Brick::printPreBasic($_GET)."\n #End of _GET enviroment  <<<<<<<<<<<<<<<<<<<<<<<<<<< # \n\n");
                }
                if ($objectData) {
                    fputs($logFileHandle,
                        \Ease\Brick::printPreBasic($objectData)."\n #End of ObjectData >>>>>>>>>>>>>>>>>>>>>>>>>>>># \n\n");
                }
                fclose($logFileHandle);
            } else {
                $this->addToLog('Error: Couldn\'t open the '.realpath($this->errorLogFile).' error log file',
                    'error');
            }
        }
        $this->addToLog($caller, $message, 'error');
    }

    /**
     * Uzavře chybové soubory.
     */
    public function __destruct()
    {
        if ($this->_logFileHandle) {
            fclose($this->_logFileHandle);
        }
        if ($this->_errorLogFileHandle) {
            fclose($this->_errorLogFileHandle);
        }
    }

    /**
     * Vrací styl logování.
     *
     * @param string $logType typ logu warning|info|success|error|notice|*
     *
     * @return string
     */
    public function getLogStyle($logType = 'notice')
    {
        if (key_exists($logType, $this->logStyles)) {
            return $this->logStyles[$logType];
        } else {
            return '';
        }
    }
}


<?php
/**
 * @author    Labs64 <netlicensing@labs64.com>
 * @license   Apache-2.0
 * @link      http://netlicensing.io
 * @copyright 2017 Labs64 NetLicensing
 */

namespace NetLicensing;

/**
 * PHP representation of the Licensee Service. See NetLicensingAPI for details:
 * https://www.labs64.de/confluence/display/NLICPUB/Licensee+Services
 *
 * @package NetLicensing
 */
class LicenseeService
{
    /**
     * Creates new licensee object with given properties.See NetLicensingAPI for details:
     * https://www.labs64.de/confluence/display/NLICPUB/Licensee+Services#LicenseeServices-Createlicensee
     *
     * determines the vendor on whose behalf the call is performed
     * @param Context $context
     *
     * parent product to which the new licensee is to be added
     * @param $productNumber
     *
     * non-null properties will be taken for the new object, null properties will either stay null, or will
     * be set to a default value, depending on property.
     * @param Licensee $licensee
     *
     * return the newly created licensee object
     * @return Licensee|null
     * @throws MalformedArgumentsException
     * @throws \ErrorException
     * @throws RestException
     */
    public static function create(Context $context, $productNumber, Licensee $licensee)
    {
        CheckUtils::paramNotEmpty($productNumber, Constants::PRODUCT_NUMBER);

        $licensee->setProperty(Constants::PRODUCT_NUMBER, $productNumber);

        $response = NetLicensingService::getInstance()
            ->post($context, Constants::LICENSEE_ENDPOINT_PATH, $licensee->asPropertiesMap());

        $createdLicensee = null;

        if (!empty($response->items->item[0])) {
            $createdLicensee = ItemToLicenseeConverter::convert($response->items->item[0]);
            $createdLicensee->exists = true;
        }

        return $createdLicensee;
    }

    /**
     * Gets licensee by its number.See NetLicensingAPI for details:
     * https://www.labs64.de/confluence/display/NLICPUB/Licensee+Services#LicenseeServices-Getlicensee
     *
     * determines the vendor on whose behalf the call is performed
     * @param Context $context
     *
     * the licensee number
     * @param $number
     *
     * return the licensee
     * @return Licensee|null
     * @throws MalformedArgumentsException
     * @throws \ErrorException
     * @throws RestException
     */
    public static function get(Context $context, $number)
    {
        CheckUtils::paramNotEmpty($number, Constants::NUMBER);

        $response = NetLicensingService::getInstance()
            ->get($context, Constants::LICENSEE_ENDPOINT_PATH . '/' . $number);

        $licensee = null;

        if (!empty($response->items->item[0])) {
            $licensee = ItemToLicenseeConverter::convert($response->items->item[0]);
            $licensee->exists = true;
        }

        return $licensee;
    }

    /**
     * Returns all licensees of a vendor.See NetLicensingAPI for details:
     * https://www.labs64.de/confluence/display/NLICPUB/Licensee+Services#LicenseeServices-Licenseeslist
     *
     * determines the vendor on whose behalf the call is performed
     * @param Context $context
     *
     * reserved for the future use, must be omitted / set to NULL
     * @param null $filter
     *
     * array of licensees (of all products) or empty array if nothing found.
     * @return Page
     * @throws \ErrorException
     * @throws RestException
     */
    public static function getList(Context $context, $filter = null)
    {
        $queryParams = (!is_null($filter)) ? [Constants::FILTER => $filter] : [];

        $response = NetLicensingService::getInstance()
            ->get($context, Constants::LICENSEE_ENDPOINT_PATH, $queryParams);

        $licensees = [];
        $pageNumber = !empty($response->items->pagenumber) ? $response->items->pagenumber : 0;
        $itemsNumber = !empty($response->items->itemsnumber) ? $response->items->itemsnumber : 0;
        $totalPages = !empty($response->items->totalpages) ? $response->items->totalpages : 0;
        $totalItems = !empty($response->items->totalitems) ? $response->items->totalitems : 0;

        if (!empty($response->items->item)) {
            foreach ($response->items->item as $item) {
                $licensee = ItemToLicenseeConverter::convert($item);
                $licensee->exists = true;

                $licensees[] = $licensee;
            }
        }

        return new Page($licensees, $pageNumber, $itemsNumber, $totalPages, $totalItems);
    }

    /**
     * Updates licensee properties.See NetLicensingAPI for details:
     * https://www.labs64.de/confluence/display/NLICPUB/Licensee+Services#LicenseeServices-Updatelicensee
     *
     * determines the vendor on whose behalf the call is performed
     * @param Context $context
     *
     * licensee number
     * @param $number
     *
     * non-null properties will be updated to the provided values, null properties will stay unchanged.
     * @param Licensee $licensee
     *
     * return updated licensee.
     * @return Licensee|null
     * @throws MalformedArgumentsException
     * @throws \ErrorException
     * @throws RestException
     */
    public static function update(Context $context, $number, Licensee $licensee)
    {
        CheckUtils::paramNotEmpty($number, Constants::NUMBER);

        $response = NetLicensingService::getInstance()
            ->post($context, Constants::LICENSEE_ENDPOINT_PATH . '/' . $number, $licensee->asPropertiesMap());

        $updatedLicensee = null;

        if (!empty($response->items->item[0])) {
            $updatedLicensee = ItemToLicenseeConverter::convert($response->items->item[0]);
            $updatedLicensee->exists = true;
        }

        return $updatedLicensee;
    }

    /**
     * Deletes licensee.See NetLicensingAPI for details:
     * https://www.labs64.de/confluence/display/NLICPUB/Licensee+Services#LicenseeServices-Deletelicensee
     *
     * determines the vendor on whose behalf the call is performed
     * @param Context $context
     *
     * licensee number
     * @param $number
     *
     * if true, any entities that depend on the one being deleted will be deleted too
     * @param bool $forceCascade
     *
     * @return bool
     * @throws MalformedArgumentsException
     * @throws \ErrorException
     * @throws RestException
     */
    public static function delete(Context $context, $number, $forceCascade = false)
    {
        CheckUtils::paramNotEmpty($number, Constants::NUMBER);

        $queryParams[Constants::CASCADE] = ((bool)$forceCascade) ? 'true' : 'false';

        return NetLicensingService::getInstance()
            ->delete($context, Constants::LICENSEE_ENDPOINT_PATH . '/' . $number, $queryParams);
    }

    /**
     * Validates active licenses of the licensee. See NetLicensingAPI for details:
     * https://www.labs64.de/confluence/display/NLICPUB/Licensee+Services#LicenseeServices-Validatelicensee
     *
     * @param Context $context
     *
     * licensee number
     * @param string $number
     *
     * optional validation parameters. See ValidationParameters and licensing model documentation for
     * details.
     * @param ValidationParameters $validationParameters
     *
     * @return ValidationResults
     * @throws MalformedArgumentsException
     * @throws RestException
     * @throws \ErrorException
     */

    public static function validate(Context $context, $number, ValidationParameters $validationParameters)
    {
        CheckUtils::paramNotEmpty($number, 'number');

        $queryParams = [];

        if ($validationParameters->getProductNumber()) {
            $queryParams[Constants::PRODUCT_NUMBER] = $validationParameters->getProductNumber();
        }

        if ($validationParameters->getLicenseeName()) {
            $queryParams[Constants::LICENSEE_PROP_LICENSEE_NAME] = $validationParameters->getLicenseeName();
        }

        if ($validationParameters->getLicenseeSecret()) {
            $queryParams[Constants::LICENSEE_PROP_LICENSEE_SECRET] = $validationParameters->getLicenseeSecret();
        }

        $pmIndex = 0;

        foreach ($validationParameters->getParameters() as $productModuleName => $parameters) {
            $queryParams[Constants::PRODUCT_MODULE_NUMBER . $pmIndex] = $productModuleName;
            foreach ($parameters as $key => $value) {
                $queryParams[$key . $pmIndex] = $value;
            }
            $pmIndex++;
        }

        $urlTemplate = Constants::LICENSEE_ENDPOINT_PATH . '/' . $number . '/' . Constants::LICENSEE_ENDPOINT_PATH_VALIDATE;

        $response = NetLicensingService::getInstance()->post($context, $urlTemplate, $queryParams);

        $validationResults = new ValidationResults();

        if (!empty($response->items->item)) {
            foreach ($response->items->item as $item) {
                $array = ItemToArrayConverter::convert($item);
                $validationResults->setProductModuleValidation($array[Constants::PRODUCT_MODULE_NUMBER], $array);
            }

            $validationResults->setTtl(new \DateTime($response->ttl));
        }

        return $validationResults;
    }

    /**
     * Transfer licenses between licensees.
     * https://www.labs64.de/confluence/display/NLICPUB/Licensee+Services#LicenseeServices-Transferlicensee
     *
     * determines the vendor on whose behalf the call is performed
     * @param Context $context
     *
     * the number of the licensee receiving licenses
     * @param $number
     *
     * the number of the licensee delivering licenses
     * @param $sourceLicenseeNumber
     *
     * @return void
     * @throws MalformedArgumentsException
     * @throws \ErrorException
     * @throws RestException
     */
    public static function transfer(Context $context, $number, $sourceLicenseeNumber)
    {
        CheckUtils::paramNotEmpty($number, Constants::NUMBER);
        CheckUtils::paramNotEmpty($sourceLicenseeNumber, Constants::LICENSEE_SOURCE_LICENSEE_NUMBER);

        $queryParams[Constants::LICENSEE_SOURCE_LICENSEE_NUMBER] = $sourceLicenseeNumber;

        NetLicensingService::getInstance()
            ->post($context, Constants::LICENSEE_ENDPOINT_PATH . '/' . $number . '/' . Constants::LICENSEE_ENDPOINT_PATH_TRANSFER, $queryParams);
    }
}


<?php

namespace Ease\TWB;

class Modal extends \Ease\Html\DivTag
{
    /**
     * Spodek dialogu s tlačítky.
     *
     * @var \Ease\Html\DivTag
     */
    public $footer;

    /**
     * Vlastnosti dialogu.
     *
     * @var array
     */
    private $properties;

    /**
     * Jméno dialogu.
     *
     * @var string
     */
    public $name;

    /**
     * Titulek dialogu.
     *
     * @var string
     */
    public $title;

    /**
     * Tělo dialogu.
     *
     * @var \Ease\Html\DivTag
     */
    public $body;

    /**
     * Hlavička dialogu.
     *
     * @var \Ease\Html\DivTag
     */
    public $header;

    /**
     * Vytvoří modální dialogs.
     *
     * @param string $name
     * @param mixed  $content
     * @param array  $properties
     */
    public function __construct($name, $title, $content = null, $properties = [])
    {
        parent::__construct(null,
            ['class' => 'modal fade', 'id' => $name, 'tabindex' => '-1', 'role' => 'dialog',
                'aria-labelledby' => $title.'ID', 'aria-hidden' => 'true',]);
        $this->properties = $properties;
        $this->name       = $name;
        $this->title      = $title;
        $this->header     = new \Ease\Html\DivTag(null, ['class' => 'modal-header']);
        $this->header->addItem(new \Ease\Html\ButtonTag('×',
                ['class' => 'close', 'data-dismiss' => 'modal', 'aria-hidden' => 'true']));
        $this->body       = new \Ease\Html\DivTag($content,
            ['class' => 'modal-body']);
        $this->footer     = new \Ease\Html\DivTag(null, ['class' => 'modal-footer']);
        $this->footer->addItem(new \Ease\Html\ButtonTag(_('Close'),
                ['id' => $name.'ko', 'type' => 'button', 'class' => 'btn btn-default',
                'data-dismiss' => 'modal',]));
        $this->footer->addItem(new \Ease\Html\ButtonTag(_('Save'),
                ['id' => $name.'ok', 'type' => 'button', 'class' => 'btn btn-primary']));
    }

    /**
     * Finalize modal.
     */
    public function finalize()
    {
        Part::twBootstrapize();
        $modalDialog  = $this->addItem(new \Ease\Html\DivTag(null,
                ['class' => 'modal-dialog', 'role' => 'document']));
        $modalContent = $modalDialog->addItem(new \Ease\Html\DivTag(null,
                ['class' => 'modal-content']));
        $this->header->addItem(new \Ease\Html\H4Tag($this->title,
                ['class' => 'modal-title', 'id' => $this->title.'ID']));
        $modalContent->addItem($this->header);
        $modalContent->addItem($this->body);
        $modalContent->addItem($this->footer);
        if (is_array($this->properties)) {
            \Ease\Shared::webPage()->addJavaScript(
                ' $(function ()
{
    $("#'.$this->name.'").modal( {'.Part::partPropertiesToString($this->properties).'});
});
', null, true
            );
        } else {
            \Ease\Shared::webPage()->addJavaScript(
                ' $(function ()
{
    $("#'.$this->name.'").modal( '.$this->properties.');
});
', null, true
            );
        }
    }
}


<?php
/**
 * @author    Labs64 <netlicensing@labs64.com>
 * @license   Apache-2.0
 * @link      http://netlicensing.io
 * @copyright 2017 Labs64 NetLicensing
 */

namespace NetLicensing;

/**
 * PHP representation of the Transaction Service. See NetLicensingAPI for details:
 * https://www.labs64.de/confluence/display/NLICPUB/Transaction+Services
 *
 * Transaction is created each time change to  LicenseService licenses happens. For instance licenses are
 * obtained by a licensee, licenses disabled by vendor, licenses deleted, etc. Transaction is created no matter what
 * source has initiated the change to licenses: it can be either a direct purchase of licenses by a licensee via
 * NetLicensing Shop, or licenses can be given to a licensee by a vendor. Licenses can also be assigned implicitly by
 * NetLicensing if it is defined so by a license model (e.g. evaluation license may be given automatically). All these
 * events are reflected in transactions. Of all the transaction handling routines only read-only routines are exposed to
 * the public API, as transactions are only allowed to be created and modified by NetLicensing internally.
 *
 * @package NetLicensing
 */
class TransactionService
{
    /**
     * Creates new transaction object with given properties.See NetLicensingAPI for details:
     * https://www.labs64.de/confluence/display/NLICPUB/Transaction+Services#TransactionServices-Createtransaction
     *
     * determines the vendor on whose behalf the call is performed
     * @param Context $context
     *
     * non-null properties will be taken for the new object, null properties will either stay null, or will
     * be set to a default value, depending on property.
     * @param Transaction $transaction
     *
     * return the newly created transaction object
     * @return Transaction|null
     * @throws \ErrorException
     * @throws RestException
     */
    public static function create(Context $context, Transaction $transaction)
    {
        $response = NetLicensingService::getInstance()
            ->post($context, Constants::TRANSACTION_ENDPOINT_PATH, $transaction->asPropertiesMap());

        $createdTransaction = null;

        if (!empty($response->items->item[0])) {
            $createdTransaction = ItemToTransactionConverter::convert($response->items->item[0]);
            $createdTransaction->exists = true;
        }

        return $createdTransaction;
    }

    /**
     * Gets transaction by its number.See NetLicensingAPI for details:
     * https://www.labs64.de/confluence/display/NLICPUB/Transaction+Services#TransactionServices-Gettransaction
     *
     * determines the vendor on whose behalf the call is performed
     * @param Context $context
     *
     * the transaction number
     * @param $number
     *
     * return the transaction
     * @return Transaction|null
     * @throws MalformedArgumentsException
     * @throws \ErrorException
     * @throws RestException
     */
    public static function get(Context $context, $number)
    {
        CheckUtils::paramNotEmpty($number, Constants::NUMBER);

        $response = NetLicensingService::getInstance()
            ->get($context, Constants::TRANSACTION_ENDPOINT_PATH . '/' . $number);

        $transaction = null;

        if (!empty($response->items->item[0])) {
            $transaction = ItemToTransactionConverter::convert($response->items->item[0]);
            $transaction->exists = true;
        }

        return $transaction;
    }

    /**
     * Returns all transactions of a vendor.See NetLicensingAPI for details:
     * https://www.labs64.de/confluence/display/NLICPUB/Transaction+Services#TransactionServices-Transactionslist
     *
     * determines the vendor on whose behalf the call is performed
     * @param \NetLicensing\Context $context
     *
     * reserved for the future use, must be omitted / set to NULL
     * @param string $filter
     *
     * array of transaction entities or empty array if nothing found.
     * @return Page
     * @throws \ErrorException
     * @throws RestException
     */
    public static function getList(Context $context, $filter = null)
    {
        $queryParams = (!is_null($filter)) ? [Constants::FILTER => $filter] : [];

        $response = NetLicensingService::getInstance()
            ->get($context, Constants::TRANSACTION_ENDPOINT_PATH, $queryParams);

        $transactions = [];
        $pageNumber = !empty($response->items->pagenumber) ? $response->items->pagenumber : 0;
        $itemsNumber = !empty($response->items->itemsnumber) ? $response->items->itemsnumber : 0;
        $totalPages = !empty($response->items->totalpages) ? $response->items->totalpages : 0;
        $totalItems = !empty($response->items->totalitems) ? $response->items->totalitems : 0;

        if (!empty($response->items->item)) {
            foreach ($response->items->item as $item) {
                $transaction = ItemToTransactionConverter::convert($item);
                $transaction->exists = true;

                $transactions[] = $transaction;
            }
        }

        return new Page($transactions, $pageNumber, $itemsNumber, $totalPages, $totalItems);
}

    /**
     * Updates transaction properties.See NetLicensingAPI for details:
     * https://www.labs64.de/confluence/display/NLICPUB/Transaction+Services#TransactionServices-Updatetransaction
     *
     * determines the vendor on whose behalf the call is performed
     * @param \NetLicensing\Context $context
     *
     * transaction number
     * @param string $number
     *
     * non-null properties will be updated to the provided values, null properties will stay unchanged.
     * @param \NetLicensing\Transaction $transaction
     *
     * return updated transaction.
     * @return Transaction|null
     * @throws MalformedArgumentsException
     * @throws \ErrorException
     * @throws RestException
     */
    public static function update(Context $context, $number, Transaction $transaction)
    {
        CheckUtils::paramNotEmpty($number, Constants::NUMBER);

        $response = NetLicensingService::getInstance()
            ->post($context, Constants::TRANSACTION_ENDPOINT_PATH . '/' . $number, $transaction->asPropertiesMap());

        $updatedTransaction = null;

        if (!empty($response->items->item[0])) {
            $updatedTransaction = ItemToTransactionConverter::convert($response->items->item[0]);
            $updatedTransaction->exists = true;
        }

        return $updatedTransaction;
      }
}


<?php

namespace Ease\Html;

/**
 * Show search input field
 * Zobrazí vyhledávací poloíčko.
 *
 * @author Vítězslav Dvořák <vitex@hippy.cz>
 */
class InputSearchTag extends InputTag
{
    /**
     * URL zdroje dat pro hinter.
     *
     * @var string
     */
    public $dataSourceURL = null;

    /**
     * Zobrazí tag pro vyhledávací box.
     *
     * @param string $name       jméno
     * @param string $value      předvolená hodnota
     * @param array  $properties dodatečné vlastnosti tagu
     */
    public function __construct($name, $value = null, $properties = [])
    {
        $properties['type'] = 'search';
        if ($value) {
            $properties['value'] = $value;
        }
        if ($name) {
            $properties['name'] = $name;
        }
        if (!isset($properties['id'])) {
            $this->setTagID($name.\Ease\Brick::randomString());
        }
        $this->setTagProperties($properties);
        parent::__construct($name, $value);
    }

    /**
     * Nastaví zdroj dat našeptávače.
     *
     * @param string $dataSourceURL url zdroje dat našeptávače ve formátu JSON
     */
    public function setDataSource($dataSourceURL)
    {
        $this->dataSourceURL = $dataSourceURL;
    }

    /**
     * Vloží do stránky scripty pro hinter.
     */
    public function finalize()
    {
        if (!is_null($this->dataSourceURL)) {
            \Ease\JQuery\UIPart::jQueryze();
            $this->addCSS('.ui-autocomplete-loading { background: white url(\'Ease/css/images/ui-anim_basic_16x16.gif\') right center no-repeat; }');
            $this->addJavaScript(
                '
    $( "#'.$this->getTagID().'" ).bind( "keydown", function (event) {
            if ( event.keyCode === $.ui.keyCode.TAB &&
                            $( this ).data( "autocomplete" ).menu.active ) {
                    event.preventDefault();
            }
    })
    .autocomplete({
            source: function (request, response) {
                    $.getJSON( "'.$this->dataSourceURL.'", { term: request.term }, response );
            },
            focus: function () {
                    // prevent value inserted on focus
                    return false;
            },
            open: function () {
                    $( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" );
            },
            close: function () {
                    $( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" );
            }
    });

            ', null, true
            );
        }
    }
}


<?php
/**
 * Created by PhpStorm.
 * User: minhao
 * Date: 2016-02-05
 * Time: 14:42
 */

namespace Oasis\SlimApp\SentinelCommand;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class CommandRunner
{
    /** @var  Application */
    protected $application;
    /** @var  int */
    protected $parallelIndex;

    protected $name;
    /** @var  InputInterface */
    protected $input;
    /** @var  OutputInterface */
    protected $output;

    protected $once           = false;
    protected $interval       = 0;
    protected $frequency      = 0;
    protected $frequencyFixed = false;

    protected $lastRun      = 0;
    protected $nextRun      = 0;
    protected $currentPid   = 0;
    protected $alert        = true;
    protected $stopped      = false;
    protected $traceEnabled = false;

    public function __construct(Application $application,
                                $parallelIndex,
                                array $command,
                                OutputInterface $output,
                                $traceEnabled = false)
    {
        $this->application   = $application;
        $this->parallelIndex = $parallelIndex;

        $this->output = $output;

        $this->name = $command['name'];
        $args       = ['command' => $this->name];
        $args       = array_merge($args, $command['args']);
        //mdebug("args = %s", json_encode($args));
        $args = array_map(
            function ($argValue) {
                if ($argValue === "\$PARALLEL_INDEX") {
                    return $this->parallelIndex;
                }
                else {
                    return $argValue;
                }
            },
            $args
        );
        //mdebug("args = %s", json_encode($args));
        $this->input = new ArrayInput($args);

        $this->once           = $command['once'];
        $this->interval       = $command['interval'];
        $this->frequency      = $command['frequency'];
        $this->frequencyFixed = $command['frequency_fixed'];
        $this->alert          = $command['alert'];

        $this->nextRun      = time();
        $this->traceEnabled = $traceEnabled;
    }

    public function shouldStartNextRunWhenNotFinished()
    {
        if (!$this->frequencyFixed || !$this->frequency || $this->once) {
            return false;
        }

        if ($this->lastRun + $this->frequency <= time()) {
            //\mnotice("will start early: %d + %d <= %d", $this->lastRun, $this->frequency, time());

            return true;
        }
        else {
            return false;
        }
    }

    /**
     * clones an early runner when frequency is reached before current run finishes
     */
    public function cloneEarlyRunner()
    {
        $ret          = clone $this;
        $ret->nextRun = time();
        $this->once   = true;

        //\mdebug('Cloned early runner for process [%d]', $this->currentPid);

        return $ret;
    }

    public function __clone()
    {
        $this->lastRun    = 0;
        $this->nextRun    = 0;
        $this->currentPid = 0;
    }

    public function onProcessExit($exitStatus, $pid)
    {
        if ($exitStatus != 0) {
            if ($this->alert) {
                malert("Daemon command %s failed with exit code = %d", $this->name, $exitStatus);
            }
            else {
                mwarning("Daemon command %s failed with exit code = %d", $this->name, $exitStatus);
            }
        }

        if ($this->once) {
            $this->stopped = true;
        }
        else {
            $this->nextRun = time();

            if ($this->frequency) {
                if ($this->nextRun - $this->lastRun < $this->frequency) {
                    $this->nextRun = $this->lastRun + $this->frequency;
                }
            }

            if ($this->interval && $this->nextRun - time() < $this->interval) {
                $this->nextRun = time() + $this->interval;
            }
        }

        if ($this->traceEnabled) {
            mdebug(
                "Process [%d] exits for command %s, exit code = %d, last run = %d, next run = %d",
                $pid,
                $this->name,
                $exitStatus,
                $this->lastRun,
                $this->nextRun
            );
        }
    }

    public function run()
    {
        if ($this->stopped) {
            return 0;
        }

        $pid = pcntl_fork();
        if ($pid < 0) {
            $errno = pcntl_get_last_error();
            throw new \RuntimeException("Cannot fork process, error = " . pcntl_strerror($errno));
        }
        elseif ($pid == 0) {
            // in child process
            $now = time();
            if ($now < $this->nextRun) {
                if ($this->traceEnabled) {
                    mdebug("Will wait %d seconds for next run of %s", $this->nextRun - $now, $this->name);
                }
                sleep($this->nextRun - $now);
            }

            // run using application
            $this->application->setAutoExit(false); // we will handle exit on our own
            $this->application->setCatchExceptions(false); // we will catch on our own
            try {
                $ret = $this->application->run($this->input, $this->output);
            } catch (\Exception $e) {
                mtrace($e, "Exception while running command {$this->name}", "error");
                $ret = DaemonSentinelCommand::EXIT_CODE_COMMON_ERROR;
            }

            // Check if we should alert
            if ($ret != DaemonSentinelCommand::EXIT_CODE_OK
                && $this->alert
            ) {
                // alert in child process is better, because we can get more trace here
                malert("Daemon command %s failed with exit code = %d", $this->name, $ret);

                exit(DaemonSentinelCommand::EXIT_CODE_OK); // exit OK because alert is already sent
            }
            else {
                exit($ret);
            }
        }
        else {
            $this->lastRun    = $this->nextRun;
            $this->currentPid = $pid;

            //\mdebug("Started process %d", $pid);

            return $pid;
        }
    }
}


<?php

namespace Ease\TWB;

/**
 * Create TWBootstrap tabs.
 *
 * @see    http://getbootstrap.com/2.3.2/components.html#navs
 *
 * @author Vítězslav Dvořák <vitex@hippy.cz>
 */
class Tabs extends \Ease\Container
{
    /**
     * Název.
     *
     * @var string
     */
    public $partName = 'TWBTabs';

    /**
     * Array of tab names=>contents.
     *
     * @var array
     */
    public $tabs = [];

    /**
     * Jméno aktivního tabu.
     *
     * @var string
     */
    private $activeTab = null;

    /**
     * Create TWBootstrap tabs.
     *
     * @param string $partName       - DIV id
     * @param array  $tabsList
     * @param array  $tagProperties
     */
    public function __construct($partName, $tabsList = null,
                                $tagProperties = null)
    {
        $this->partName = $partName;
        parent::__construct();
        if (is_array($tabsList)) {
            $this->tabs = array_merge($this->tabs, $tabsList);
        }
        if (!is_null($tagProperties)) {
            $this->setPartProperties($tagProperties);
        }
    }

    /**
     * Vytvoří nový tab a vloží do něj obsah.
     *
     * @param string $tabName    jméno a titulek tabu
     * @param mixed  $tabContent
     * @param bool   $active     Má být tento tab aktivní ?
     *
     * @return \Ease\Html\DivTag odkaz na vložený obsah
     */
    public function &addTab($tabName, $tabContent = null, $active = false)
    {
        if (is_null($tabContent)) {
            $tabContent = new \Ease\Html\DivTag();
        }
        $this->tabs[$tabName] = ['static' => $tabContent];
        if ($active) {
            $this->activeTab = $tabName;
        }

        return $this->tabs[$tabName]['static'];
    }

    /**
     * Create new Dynamic Tab
     *
     * @param string $tabName    jméno a titulek tabu
     * @param string $tabUrl     where to obtain tab content
     * @param bool   $active     Má být tento tab aktivní ?
     *
     * @return \Ease\Html\DivTag odkaz na vložený obsah
     */
    public function &addAjaxTab($tabName, $tabUrl, $active = false)
    {
        $this->tabs[$tabName] = ['ajax' => $tabUrl];
        if ($active) {
            $this->activeTab = $tabName;
        }
        \Ease\Shared::webPage()->addJavaScript('
$(\'#'.$this->getTagID().' a\').click(function (e) {
	e.preventDefault();

	var url = $(this).attr("data-url");
  	var href = this.hash;
  	var pane = $(this);

	// ajax load from data-url
	$(href).load(url,function(result){
	    pane.tab(\'show\');
	});
});

');
        return $this->tabs[$tabName];
    }

    /**
     * Vrací ID tagu.
     *
     * @return string
     */
    public function getTagID()
    {
        return $this->partName;
    }

    /**
     * Vložení skriptu a divů do stránky.
     */
    public function finalize()
    {
        if (is_null($this->activeTab)) {
            $this->activeTab = current(array_keys($this->tabs));
        }
        $tabsUl = $this->addItem(new \Ease\Html\UlTag(null,
                ['class' => 'nav nav-tabs', 'id' => $this->partName]));
        foreach ($this->tabs as $tabName => $tab) {
            $tabProperties = ['data-toggle' => 'tab'];
            if (key($tab) == 'ajax') {
                $tabProperties['data-url'] = current($tab);
            }
            $anchor = '#'.\Ease\Brick::lettersOnly( str_replace(['(',')'],'',$this->partName.$tabName));
            if ($tabName == $this->activeTab) {
                $tabsUl->addItem(new \Ease\Html\LiTag(new \Ease\Html\ATag($anchor,
                            $tabName, $tabProperties), ['class' => 'active']));
            } else {
                $tabsUl->addItem(new \Ease\Html\LiTag(new \Ease\Html\ATag($anchor,
                            $tabName, $tabProperties)));
            }
        }
        $tabDiv = $this->addItem(new \Ease\Html\DivTag(null,
                ['id' => $this->partName.'body', 'class' => 'tab-content']));
        foreach ($this->tabs as $tabName => $tab) {
            switch (key($tab)) {
                case 'static':
                    $tabContent = current($tab);
                    break;
                case 'ajax':
                    $tabContent = '';
                    break;
            }

            if ($tabName == $this->activeTab) {
                $tabDiv->addItem(new \Ease\Html\DivTag($tabContent,
                        ['id' => $this->partName.\Ease\Brick::lettersOnly($tabName),
                        'class' => 'tab-pane active',]));
            } else {
                $tabDiv->addItem(new \Ease\Html\DivTag($tabContent,
                        ['id' => $this->partName.\Ease\Brick::lettersOnly($tabName),
                        'class' => 'tab-pane',]));
            }
        }
        Part::twBootstrapize();

        if (key($this->tabs[$this->activeTab]) == 'ajax') {
            \Ease\Shared::webPage()->addJavaScript('
// load first tab content
$(\'#'.$this->partName.$this->activeTab.'\').load($(\'.active a\').attr("data-url"),function(result){
  $(\'.active a\').tab(\'show\');
});
');
        } else {
            \Ease\Shared::webPage()->addJavaScript(
                '
        $(\'#'.$this->partName.' a[href="#'.\Ease\Brick::lettersOnly($this->activeTab).'"]\').tab(\'show\');
', null, true
            );
        }
    }
}


<?php
namespace Paradox;

/**
 * Paradox is an elegant Object Document Mananger (ODM) to use with the ArangoDB Document/Graph database server.
 * Paradox requires ArangoDB-PHP to communication with the server, so it needs to be installed and avaliable.
 *
 * Debug
 * The debugger provides debugging on the global basis. It prints the raw HTTP request that is sent to the server, and the raw HTTP response received.
 *
 * @version 1.3.0
 *
 * @author Francis Chuang <francis.chuang@gmail.com>
 * @link https://github.com/F21/Paradox
 * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache 2 License
 */
class Debug
{
    /**
     * Whether the debugger is on or off.
     * @var boolean
     */
    protected $_debug;

    /**
     * Sets up the debugger and turn the debugger on or off.
     * @param string $debug Set to turn the debugger on or off.
     */
    public function __construct($debug = false)
    {
        $this->_debug = (bool) $debug;
    }

    /**
     * Allows this object to be called as a function to call the trace method.
     * @param string                                                           $type Whether the debug message was for sending a request or receiving a response.
     * @param \triagens\ArangoDb\TraceResponse|\triagens\ArangoDb\TraceRequest $data The raw HTTP request or response.
     */
    public function __invoke($data)
    {
        $this->trace($data);
    }

    /**
     * Turns the debugger on or off.
     * @param boolean $value
     */
    public function setDebug($value)
    {
        $this->_debug = (bool) $value;
    }

    /**
     * Outputs the debug message.
     * @param \triagens\ArangoDb\TraceResponse|\triagens\ArangoDb\TraceRequest $data The HTTP request or response trace object.
     */
    protected function trace($data)
    {
        if ($this->_debug) {

            //Open the tags and output request specific items
            if ($data->getType() == "request") {
                print '

';
                print "
Request to server
";
                print "
{$data->getMethod()} {$data->getRequestUrl()}
";
            }

            //Output response specific items
            if ($data->getType() == "response") {
                print '
Response from server
';

                if ($data->getHttpCode() >= 400) {
                    print '
' . $data->getHttpCode() . ' ' . $data->getHttpCodeDefinition() . '
';
                } else {
                    print '
' . $data->getHttpCode() . ' ' . $data->getHttpCodeDefinition() . '
';
                }

            }

            //Headers
            print "
";
            foreach ($data->getHeaders() as $header => $value) {
                 print "$header : $value\n";
            }
            print "
";

            //Body
            if ($data->getBody()) {
                $body = json_decode($data->getBody());
                print "
" . json_encode($body, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "
";
            }

            //Close off the tags
            if ($data->getType() == "request") {
                print '	';
            }

            if ($data->getType() == "response") {
                print '
Time taken: ' . $data->getTimeTaken() * 1000 . ' ms
';
                print "

";
            }
        }
    }
}


<?php

declare(strict_types=1);

/*
 * (c) Christian Gripp <mail@core23.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Core23\LastFm\Service;

use Core23\LastFm\Client\ApiClientInterface;
use Core23\LastFm\Exception\ApiException;
use Core23\LastFm\Exception\NotFoundException;
use Core23\LastFm\Session\Session;
use Core23\LastFm\Session\SessionInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\NullLogger;

final class AuthService implements LoggerAwareInterface, AuthServiceInterface
{
    use LoggerAwareTrait;

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

    /**
     * @var ApiClientInterface
     */
    private $client;

    /**
     * @param ApiClientInterface $connection
     * @param string             $authUrl
     */
    public function __construct(ApiClientInterface $connection, string $authUrl = 'http://www.last.fm/api/auth/')
    {
        $this->client  = $connection;
        $this->authUrl = $authUrl;
        $this->logger  = new NullLogger();
    }

    /**
     * {@inheritdoc}
     */
    public function createSession(string $token): ?SessionInterface
    {
        try {
            $response = $this->client->signedCall('auth.getSession', [
                'token' => $token,
            ]);

            return new Session($response['session']['name'], $response['session']['key'], $response['session']['subscriber']);
        } catch (ApiException $e) {
            $this->logger->warning(sprintf('Error getting session for "%s" token.', $token), [
                'exception' => $e,
            ]);
        } catch (NotFoundException $e) {
            $this->logger->info(sprintf('No session was found for "%s" token.', $token), [
                'exception' => $e,
            ]);
        }

        return null;
    }

    /**
     * {@inheritdoc}
     */
    public function createToken(): ?string
    {
        $response = $this->client->signedCall('auth.getToken');

        return $response['token'] ?? null;
    }

    /**
     * {@inheritdoc}
     */
    public function getAuthUrl(string $callbackUrl): string
    {
        return $this->authUrl.'?api_key='.$this->client->getApiKey().'&cb='.$callbackUrl;
    }
}


<?php

declare(strict_types=1);

/*
 * (c) Christian Gripp <mail@core23.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Core23\LastFm\Service;

use Core23\LastFm\Client\ApiClientInterface;
use Core23\LastFm\Model\ArtistInfo;
use Core23\LastFm\Model\Song;
use Core23\LastFm\Model\Tag;
use Core23\LastFm\Util\ApiHelper;

final class ChartService implements ChartServiceInterface
{
    /**
     * @var ApiClientInterface
     */
    private $client;

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

    /**
     * {@inheritdoc}
     */
    public function getTopArtists(int $limit = 50, int $page = 1): array
    {
        $response = $this->client->unsignedCall('chart.getTopArtists', [
            'limit' => $limit,
            'page'  => $page,
        ]);

        if (!isset($response['artists']['artist'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return ArtistInfo::fromApi($data);
            },
            $response['artists']['artist']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getTopTags(int $limit = 50, int $page = 1): array
    {
        $response = $this->client->unsignedCall('chart.getTopTags', [
            'limit' => $limit,
            'page'  => $page,
        ]);

        if (!isset($response['tags']['tag'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return Tag::fromApi($data);
            },
            $response['tags']['tag']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getTopTracks(int $limit = 50, int $page = 1): array
    {
        $response = $this->client->unsignedCall('chart.getTopTracks', [
            'limit' => $limit,
            'page'  => $page,
        ]);

        if (!isset($response['tracks']['track'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return Song::fromApi($data);
            },
            $response['tracks']['track']
        );
    }
}


<?php

declare(strict_types=1);

/*
 * (c) Christian Gripp <mail@core23.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Core23\LastFm\Model;

final class Song
{
    /**
     * @var string
     */
    private $name;

    /**
     * @var int|null
     */
    private $duration;

    /**
     * @var Artist|null
     */
    private $artist;

    /**
     * @param string      $name
     * @param int|null    $duration
     * @param Artist|null $artist
     */
    public function __construct(string $name, ?int $duration, ?Artist $artist)
    {
        $this->name     = $name;
        $this->duration = $duration;
        $this->artist   = $artist;
    }

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

    /**
     * @return int|null
     */
    public function getDuration(): ?int
    {
        return $this->duration;
    }

    /**
     * @return Artist|null
     */
    public function getArtist(): ?Artist
    {
        return $this->artist;
    }

    /**
     * @param array $data
     *
     * @return Song
     */
    public static function fromApi(array $data): self
    {
        $artist = isset($data['artist']) ? Artist::fromApi($data['artist']) : null;

        return new self(
            $data['name'],
            isset($data['duration']) ? (int) $data['duration'] : null,
            $artist
        );
    }
}


<?php

declare(strict_types=1);

/*
 * (c) Christian Gripp <mail@core23.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Core23\LastFm\Service;

use Core23\LastFm\Client\ApiClientInterface;
use Core23\LastFm\Filter\Period;
use Core23\LastFm\Filter\RangeFilter;
use Core23\LastFm\Model\Album;
use Core23\LastFm\Model\Artist;
use Core23\LastFm\Model\Chart;
use Core23\LastFm\Model\Song;
use Core23\LastFm\Model\SongInfo;
use Core23\LastFm\Model\Tag;
use Core23\LastFm\Model\User;
use Core23\LastFm\Util\ApiHelper;

final class UserService implements UserServiceInterface
{
    /**
     * @var ApiClientInterface
     */
    private $client;

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

    /**
     * {@inheritdoc}
     */
    public function getArtistTracks(string $username, string $artist, ?RangeFilter $filter = null, int $page = 1): array
    {
        $query = null !== $filter ? $filter->getQuery('startTimestamp', 'endTimestamp') : [];
        $query = array_merge($query, [
            'user'           => $username,
            'artist'         => $artist,
            'page'           => $page,
        ]);

        $response = $this->client->unsignedCall('user.getArtistTracks', $query);

        if (!isset($response['artisttracks']['track'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return Song::fromApi($data);
            },
            $response['artisttracks']['track']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getFriends(string $username, bool $recenttracks = false, $limit = 50, int $page = 1): array
    {
        $response = $this->client->unsignedCall('user.getFriends', [
            'user'         => $username,
            'recenttracks' => (int) $recenttracks,
            'limit'        => $limit,
            'page'         => $page,
        ]);

        if (!isset($response['friends']['user'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return User::fromApi($data);
            },
            $response['friends']['user']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getInfo(string $username): ?User
    {
        $response =  $this->client->unsignedCall('user.getInfo', [
            'user' => $username,
        ]);

        if (!isset($response['user'])) {
            return null;
        }

        return User::fromApi($response['user']);
    }

    /**
     * {@inheritdoc}
     */
    public function getLovedTracks(string $username, int $limit = 50, int $page = 1): array
    {
        $response = $this->client->unsignedCall('user.getLovedTracks', [
            'user'  => $username,
            'limit' => $limit,
            'page'  => $page,
        ]);

        if (!isset($response['lovedtracks']['track'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return Song::fromApi($data);
            },
            $response['lovedtracks']['track']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getRecentTracks(string $username, ?RangeFilter $filter = null, $extended = false, $limit = 50, int $page = 1): array
    {
        $query = null !== $filter ? $filter->getQuery('from', 'to') : [];
        $query = array_merge($query, [
            'user'     => $username,
            'limit'    => $limit,
            'page'     => $page,
            'extended' => (int) $extended,
        ]);

        $response = $this->client->unsignedCall('user.getRecentTracks', $query);

        if (!isset($response['recenttracks']['track'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return Song::fromApi($data);
            },
            $response['recenttracks']['track']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getPersonalTagsForArtist(string $username, string $tag, int $limit = 50, int $page = 1): array
    {
        $response =  $this->client->unsignedCall('user.getPersonalTags', [
            'taggingtype' => 'artist',
            'user'        => $username,
            'tag'         => $tag,
            'limit'       => $limit,
            'page'        => $page,
        ]);

        if (!isset($response['taggings']['artists']['artist'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return Artist::fromApi($data);
            },
            $response['taggings']['artists']['artist']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getPersonalTagsForAlbum(string $username, string $tag, int $limit = 50, int $page = 1): array
    {
        $response = $this->client->unsignedCall('user.getPersonalTags', [
            'taggingtype' => 'album',
            'user'        => $username,
            'tag'         => $tag,
            'limit'       => $limit,
            'page'        => $page,
        ]);

        if (!isset($response['taggings']['albums']['album'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return Album::fromApi($data);
            },
            $response['taggings']['albums']['album']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getPersonalTagsForTracks(string $username, string $tag, int $limit = 50, int $page = 1): array
    {
        $response = $this->client->unsignedCall('user.getPersonalTags', [
            'taggingtype' => 'track',
            'user'        => $username,
            'tag'         => $tag,
            'limit'       => $limit,
            'page'        => $page,
        ]);

        if (!isset($response['taggings']['tracks']['track'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return SongInfo::fromApi($data);
            },
            $response['taggings']['tracks']['track']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getTopAlbums(string $username, Period $period, int $limit = 50, int $page = 1): array
    {
        $response = $this->client->unsignedCall('user.getTopAlbums', [
            'user'   => $username,
            'period' => $period->getValue(),
            'limit'  => $limit,
            'page'   => $page,
        ]);

        if (!isset($response['topalbums']['album'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return Album::fromApi($data);
            },
            $response['topalbums']['album']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getTopArtists(string $username, Period $period, int $limit = 50, int $page = 1): array
    {
        $response = $this->client->unsignedCall('user.getTopArtists', [
            'user'   => $username,
            'period' => $period->getValue(),
            'limit'  => $limit,
            'page'   => $page,
        ]);

        if (!isset($response['topartists']['artist'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return Artist::fromApi($data);
            },
            $response['topartists']['artist']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getTopTags(string $username, int $limit = 50): array
    {
        $response = $this->client->unsignedCall('user.getTopTags', [
            'user'  => $username,
            'limit' => $limit,
        ]);

        if (!isset($response['toptags']['tag'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return Tag::fromApi($data);
            },
            $response['toptags']['tag']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getTopTracks(string $username, Period $period, int $limit = 50, int $page = 1): array
    {
        $response = $this->client->unsignedCall('user.getTopTracks', [
            'user'   => $username,
            'period' => $period->getValue(),
            'limit'  => $limit,
            'page'   => $page,
        ]);

        if (!isset($response['toptracks']['track'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return SongInfo::fromApi($data);
            },
            $response['toptracks']['track']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getWeeklyAlbumChart(string $username, ?RangeFilter $filter = null): array
    {
        $query = null !== $filter ? $filter->getQuery('from', 'to') : [];
        $query = array_merge($query, [
            'user'     => $username,
        ]);

        $response = $this->client->unsignedCall('user.getWeeklyAlbumChart', $query);

        if (!isset($response['weeklyalbumchart']['album'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return Album::fromApi($data);
            },
            $response['weeklyalbumchart']['album']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getWeeklyArtistChart(string $username, ?RangeFilter $filter = null): array
    {
        $query = null !== $filter ? $filter->getQuery('from', 'to') : [];
        $query = array_merge($query, [
            'user'     => $username,
        ]);

        $response = $this->client->unsignedCall('user.getWeeklyArtistChart', $query);

        if (!isset($response['weeklyartistchart']['artist'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return Artist::fromApi($data);
            },
            $response['weeklyartistchart']['artist']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getWeeklyChartList(string $username): array
    {
        $response = $this->client->unsignedCall('user.getWeeklyChartList', [
            'user' => $username,
        ]);

        if (!isset($response['weeklychartlist']['chart'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return Chart::fromApi($data);
            },
            $response['weeklychartlist']['chart']
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getWeeklyTrackChart(string $username, ?RangeFilter $filter = null): array
    {
        $query = null !== $filter ? $filter->getQuery('from', 'to') : [];
        $query = array_merge($query, [
            'user'     => $username,
        ]);

        $response = $this->client->unsignedCall('user.getWeeklyTrackChart', $query);

        if (!isset($response['weeklytrackchart']['track'])) {
            return [];
        }

        return ApiHelper::mapList(
            static function ($data) {
                return SongInfo::fromApi($data);
            },
            $response['weeklytrackchart']['track']
        );
    }
}


<?php

declare(strict_types=1);

/*
 * (c) Christian Gripp <mail@core23.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Core23\LastFm\Model;

final class TagInfo
{
    /**
     * @var string
     */
    private $name;

    /**
     * @var string|null
     */
    private $url;

    /**
     * @var int
     */
    private $total;

    /**
     * @var int
     */
    private $reach;

    /**
     * @var string|null
     */
    private $wikiSummary;

    /**
     * @param string      $name
     * @param string|null $url
     * @param int         $total
     * @param int         $reach
     * @param string|null $wikiSummary
     */
    public function __construct(string $name, ?string $url, int $total, int $reach, ?string $wikiSummary)
    {
        $this->name        = $name;
        $this->url         = $url;
        $this->total       = $total;
        $this->reach       = $reach;
        $this->wikiSummary = $wikiSummary;
    }

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

    /**
     * @return string|null
     */
    public function getUrl(): ?string
    {
        return $this->url;
    }

    /**
     * @return int
     */
    public function getTotal(): int
    {
        return $this->total;
    }

    /**
     * @return int
     */
    public function getReach(): int
    {
        return $this->reach;
    }

    /**
     * @return string|null
     */
    public function getWikiSummary(): ?string
    {
        return $this->wikiSummary;
    }

    /**
     * @param array $data
     *
     * @return TagInfo
     */
    public static function fromApi(array $data): self
    {
        return new self(
            $data['name'],
            $data['url'] ?? null,
            $data['total'] ? (int) $data['total'] : 0,
            $data['reach'] ? (int) $data['reach'] : 0,
            $data['wiki']['summary'] ?? null
        );
    }
}


<?php

declare(strict_types=1);

/*
 * (c) Christian Gripp <mail@core23.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Core23\LastFm\Crawler;

use Core23\LastFm\Model\Artist;
use Core23\LastFm\Model\EventInfo;
use Core23\LastFm\Model\Venue;
use Core23\LastFm\Model\VenueAddress;
use Symfony\Component\DomCrawler\Crawler;

final class EventInfoCrawler extends AbstractCrawler implements EventInfoCrawlerInterface
{
    /**
     * {@inheritdoc}
     */
    public function getEventInfo(int $id): ?EventInfo
    {
        $node = $this->crawlEvent($id);

        if (null === $node) {
            return null;
        }

        $timeNode = $node->filter('.qa-event-date');

        return new EventInfo(
            $id,
            $this->parseString($node->filter('h1.header-title')) ?? '',
            $this->parseString($node->filter('.qa-event-description'), true),
            $this->parseDate($timeNode->filter('[itemprop="startDate"]')),
            $this->parseDate($timeNode->filter('[itemprop="endDate"]')),
            $this->parseString($node->filter('.qa-event-link a')),
            $this->parseImage($node->filter('.event-poster-preview')),
            $this->parseUrl($node->filter('link[rel="canonical"]')),
            $node->filter('.namespace--events_festival_overview')->count() > 0,
            $this->readVenues($node),
            $this->readArtists($node)
        );
    }

    /**
     * @param Crawler $node
     *
     * @return Artist[]
     */
    private function readArtists(Crawler $node): array
    {
        $artistNode = $node->filter('.grid-items');

        return $artistNode->filter('.grid-items-item')->each(function (Crawler $node): Artist {
            $image = $this->parseImage($node->filter('.grid-items-cover-image-image img'));

            return new Artist(
                $this->parseString($node->filter('.grid-items-item-main-text')) ?? '',
                null,
                $image ? [$image] : [],
                $this->parseUrl($node->filter('.grid-items-item-main-text a'))
            );
        });
    }

    /**
     * @param Crawler $node
     *
     * @return Venue
     */
    private function readVenues(Crawler $node): Venue
    {
        $venueNode   = $node->filter('.event-detail');
        $addressNode = $venueNode->filter('.event-detail-address');

        $adress = new VenueAddress(
            $this->parseString($addressNode->filter('[itemprop="streetAddress"]')),
            $this->parseString($addressNode->filter('[itemprop="postalCode"]')),
            $this->parseString($addressNode->filter('[itemprop="addressLocality"]')),
            $this->parseString($addressNode->filter('[itemprop="addressCountry"]'))
        );

        return new Venue(
            $this->parseString($venueNode->filter('[itemprop="name"]')) ?? '',
            $this->parseUrl($venueNode->filter('.event-detail .qa-event-link a')),
            $this->parseString($venueNode->filter('.event-detail-tel span')),
            $adress
        );
    }

    /**
     * @param int $id
     *
     * @return Crawler|null
     */
    private function crawlEvent(int $id): ?Crawler
    {
        $url = 'https://www.last.fm/de/event/'.$id;

        return $this->crawl($url);
    }
}


<?php
namespace Paradox;
use Paradox\exceptions\ObservableException;

/**
 * Paradox is an elegant Object Document Mananger (ODM) to use with the ArangoDB Document/Graph database server.
 * Paradox requires ArangoDB-PHP to communication with the server, so it needs to be installed and avaliable.
 *
 * Observable
 * Abstract class that implements the observer patter which allows classes that extend it to fire events to its listeners.
 *
 * @version 1.3.0
 *
 * @author Francis Chuang <francis.chuang@gmail.com>
 * @link https://github.com/F21/Paradox
 * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache 2 License
 */
abstract class AObservable
{
    /**
     * A associative array containing events and the subscribers to those events.
     * @var array
     */
    protected $_observers = array();

    /**
     * Attach a listener.
     * @param  string|array        $events   The event type. This can be an array of event types or just a string for one event.
     * @param  IObserver           $observer The listener.
     * @throws ObservableException
     */
    public function attach($events, IObserver $observer)
    {
        if (is_array($events)) {
            foreach ($events as $event) {
                $this->doAttach($event, $observer);
            }
        } elseif (is_string($events)) {
            $this->doAttach($events, $observer);
        } else {
            throw new ObservableException("Event can only be a string containing the name of the event or an array of event names.");
        }
    }

    /**
     * Do the actual work of attaching the listener.
     * @param string    $event    The event.
     * @param IObserver $observer The listener.
     */
    protected function doAttach($event, IObserver $observer)
    {
        if (!array_key_exists($event, $this->_observers)) {
            $this->_observers[$event] = array();
        }

        if (in_array($observer, $this->_observers[$event], true)) {
            return;
        }

        array_push($this->_observers[$event], $observer);
    }

    /**
     * Detatch an observer from an event.
     * @param string    $event    The event to detach from.
     * @param IObserver $observer The observer that we want to detach.
     */
    public function detach($event, IObserver $observer)
    {
        if (!isset($this->_observers[$event])) {
            return;
        }

        foreach ($this->_observers[$event] as $key=> $attachedObserver) {
            if ($attachedObserver === $observer) {
                array_splice($this->_observers[$event], $key, 1);
            }
        }
    }

    /**
     * Remove all observers for an event.
     * @param string $event The event we wish to detach.
     */
    public function detachAllObserversForEvent($event)
    {
        if (!isset($this->_observers[$event])) {
            return;
        }

        unset($this->_observers[$event]);

    }

    /**
     * Remove all events for an observer.
     * @param IObserver $observerToRemove The observer we wish to detach.
     */
    public function detachAllEventsForObserver(IObserver $observerToRemove)
    {
        foreach ($this->_observers as $event => $observers) {
            foreach ($observers as $position => $observer) {

                if ($observer === $observerToRemove) {
                    array_splice($this->_observers[$event], $position, 1);
                    break; //Save cycles by breaking here since there is each observer is only attached to each event once.
                }
            }
        }
    }

    /**
     * Notify all observers with an event and the associate object/data that observers might find useful.
     * @param string $event  The event we wish to signal.
     * @param mixed  $object The object/data that we want to make avaliable to the observers.
     */
    public function notify($event, $object)
    {
        $eventObject = new Event($event, $object);

        if (isset($this->_observers[$event]) && is_array($this->_observers[$event])) {

            foreach ($this->_observers[$event] as $observer) {

                if ($eventObject->propagationStopped()) {
                    break;
                }

                $observer->onEvent($eventObject);
            }

        }
    }
}


<?php

/*
 * (c) Christian Gripp <mail@core23.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Core23\LastFm\Builder;

use DateTime;

final class TrackBuilder
{
    /**
     * @var array
     */
    private $data;

    /**
     * @param array $data
     */
    private function __construct(array $data)
    {
        $this->data = $data;
    }

    /**
     * @param string   $artist
     * @param string   $track
     * @param DateTime $date
     *
     * @return TrackBuilder
     */
    public static function create(string $artist, string $track, DateTime $date): self
    {
        return new static([
            'artist'    => $artist,
            'track'     => $track,
            'timestamp' => $date->getTimestamp(),
        ]);
    }

    /**
     * @param string $album
     *
     * @return TrackBuilder
     */
    public function withAlbum(string $album): self
    {
        $this->data['album'] = $album;

        return $this;
    }

    /**
     * @param string $context
     *
     * @return TrackBuilder
     */
    public function withContext(string $context): self
    {
        $this->data['context'] = $context;

        return $this;
    }

    /**
     * @param int $streamId
     *
     * @return TrackBuilder
     */
    public function withStreamId(int $streamId): self
    {
        $this->data['streamId'] = $streamId;

        return $this;
    }

    /**
     * @param bool $choosenByUser
     *
     * @return TrackBuilder
     */
    public function withChosenByUser(bool $choosenByUser): self
    {
        $this->data['chosenByUser'] = $choosenByUser ? 1 : 0;

        return $this;
    }

    /**
     * @param int $trackNumber
     *
     * @return TrackBuilder
     */
    public function withTrackNumber(int $trackNumber): self
    {
        $this->data['trackNumber'] = $trackNumber;

        return $this;
    }

    /**
     * @param string $mbid
     *
     * @return TrackBuilder
     */
    public function withMbid(string $mbid): self
    {
        $this->data['mbid'] = $mbid;

        return $this;
    }

    /**
     * @param string $albumArtist
     *
     * @return TrackBuilder
     */
    public function withAlbumArtist(string $albumArtist): self
    {
        $this->data['albumArtist'] = $albumArtist;

        return $this;
    }

    /**
     * @param int $seconds
     *
     * @return TrackBuilder
     */
    public function withDuration(int $seconds): self
    {
        $this->data['duration'] = $seconds;

        return $this;
    }

    /**
     * @return array
     */
    public function getData(): array
    {
        return $this->data;
    }
}


<?php
namespace asbamboo\framework\template\extension;

use asbamboo\template\Extension;
use asbamboo\template\Functions;
use asbamboo\router\RouterInterface;
use asbamboo\framework\template\Template;
use asbamboo\router\RouteCollectionInterface;
use asbamboo\framework\Constant;

/**
 * template 模板中使用的扩展[路由相关]
 *
 * @author 李春寅 <licy2013@aliyun.com>
 * @since 2018年8月30日
 */
class RouterExtension extends Extension
{
    /**
     *
     * @var Template
     */
    private $Template;

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

    /**
     *
     * {@inheritDoc}
     * @see Extension::getFunctions()
     */
    public function getFunctions()
    {
        return [
            new Functions('path', [$this, 'path']),
            new Functions('is_current', [$this, 'isCurrent']),
        ];
    }

    /**
     *
     * @param string $route_id
     * @param array|null $params
     * @return string
     */
    public function path(string $route_id, array $params = null) : string
    {
        /**
         *
         * @var RouterInterface $Router
         */
        $Router = $this->Template->getContainer()->get(RouterInterface::class);
        return $Router->generateUrl($route_id, $params);
    }

    /**
     * 判断一个路由是否时当前正在请求的路由
     *
     * @param string $route_id
     * @return boolean
     */
    public function isCurrent(string $route_id) : bool
    {
        /**
         *
         * @var RouteCollectionInterface $RouteCollection
         */
        $RouteCollection    = $this->Template->getContainer()->get(RouterInterface::class)->getRouteCollection();
        return !empty($RouteCollection->get($route_id)->getOptions(Constant::IS_CURRENT_ROUTE));
    }
}


<?php
namespace Paradox\toolbox;
use \triagens\ArangoDb\Statement;
use \triagens\ArangoDb\Cursor;
use Paradox\Toolbox;
use Paradox\exceptions\QueryException;

/**
 * Paradox is an elegant Object Document Mananger (ODM) to use with the ArangoDB Document/Graph database server.
 * Paradox requires ArangoDB-PHP to communication with the server, so it needs to be installed and avaliable.
 *
 * Query helper
 * Provides a simple way to send AQL queries to the server and receive the response.
 *
 * @version 1.3.0
 *
 * @author Francis Chuang <francis.chuang@gmail.com>
 * @link https://github.com/F21/Paradox
 * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache 2 License
 */
class Query
{
    /**
     * A reference to the toolbox.
     * @var Toolbox
     */
    private $_toolbox;

    /**
     * Initiate the query helper.
     * @param Toolbox $toolbox
     */
    public function __construct(Toolbox $toolbox)
    {
        $this->_toolbox = $toolbox;
    }

    /**
     * Executes an AQL query and return all results. If nothing is found, an empty array is returned.
     * @param  string         $query      The AQL query to run.
     * @param  array          $parameters An optional associative array containing parameters to bind to the query.
     * @throws QueryException
     * @return array
     */
    public function getAll($query, array $parameters = array(), $includeCount = false)
    {
        if ($this->_toolbox->getTransactionManager()->hasTransaction()) {
            $statement = array('query' => $query, 'bindVars' => $parameters);

            if ($includeCount) {
                $statement['count'] = true;
                $statement = json_encode($statement, JSON_FORCE_OBJECT);
                $this->_toolbox->getTransactionManager()->addCommand("function () {var cursor = db._createStatement($statement).execute(); return {count: cursor.count(), results: cursor.elements()};}();" , "Query:getAll");
            } else {
                $statement = json_encode($statement, JSON_FORCE_OBJECT);
                $this->_toolbox->getTransactionManager()->addCommand("db._createStatement($statement).execute().elements();" , "Query:getAll");
            }

        } else {
            $data = array(
                    'query' => $query,
                    'bindVars' => $parameters,
                    Cursor::ENTRY_FLAT => true
            );

            $statement = new Statement($this->_toolbox->getConnection(), $data);

            if ($includeCount) {
                $statement->setCount(true);
            }

            try {
                $cursor = $statement->execute();
            } catch (\Exception $e) {
                $normalised = $this->_toolbox->normaliseDriverExceptions($e);
                throw new QueryException($normalised['message'], $normalised['code']);
            }

            $results = $cursor->getAll();

            if ($includeCount) {
                return array('results' => $results, 'count' => $cursor->getCount());
            } else {
                return $results;
            }
        }
    }

    /**
     * Executes an AQL query and return the first result. If nothing is found, null is returned.
     * @param  string         $query      The AQL query to run.
     * @param  array          $parameters An optional associative array containing parameters to bind to the query.
     * @throws QueryException
     * @return return         AModel
     */
    public function getOne($query, array $parameters = array())
    {
        if ($this->_toolbox->getTransactionManager()->hasTransaction()) {

            $statement = json_encode(array('query' => $query, 'bindVars' => $parameters), JSON_FORCE_OBJECT);
            $this->_toolbox->getTransactionManager()->addCommand("function () {var elements = db._createStatement($statement).execute().elements(); return elements[0] ? elements[0] : null}();" , "Query:getOne");

        } else {
            $data = array(
                    'query' => $query,
                    'bindVars' => $parameters,
                    Cursor::ENTRY_FLAT => true
            );

            $statement = new Statement($this->_toolbox->getConnection(), $data);

            try {
                $cursor = $statement->execute();
            } catch (\Exception $e) {
                $normalised = $this->_toolbox->normaliseDriverExceptions($e);
                throw new QueryException($normalised['message'], $normalised['code']);
            }

            if ($cursor->valid()) {
                $cursor->rewind();

                return $cursor->current();
            } else {
                return null;
            }
        }
    }

    /**
     * Converts query results to pods.
     * @param  string $type The type of the pod
     * @param  array  $data An array of the query result to convert.
     * @return array
     */
    public function convertToPods($type, array $data)
    {
        $converted = $this->_toolbox->getPodManager()->convertToPods($type, $data);

        foreach ($converted as $model) {
            $model->getPod()->setSaved();
        }

        return $converted;
    }

    /**
     * Returns the execution plan for a query. This will not execute the query.
     * @param  string         $query      The AQL query to run.
     * @param  array          $parameters An optional associative array containing parameters to bind to the query.
     * @throws QueryException
     * @return array
     */
    public function explain($query, array $parameters = array())
    {
        $data = array(
                'query' => $query,
                'bindVars' => $parameters
        );

        $statement = new Statement($this->_toolbox->getConnection(), $data);

        try {
            $result = $statement->explain();

            return $result['plan'];
        } catch (\Exception $e) {
            $normalised = $this->_toolbox->normaliseDriverExceptions($e);
            throw new QueryException($normalised['message'], $normalised['code']);
        }
    }
}