<?php
declare(strict_types=1);

namespace precore\util;

use ArrayObject;
use EmptyIterator;
use precore\lang\ClassCastException;
use SplHeap;

/**
 * This class consists exclusively of static methods that operate on or return collections.
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
abstract class Collections
{
    /**
     * @var Joiner
     */
    private static $STANDARD_JOINER;

    private function __construct()
    {
    }

    public static function init() : void
    {
        self::$STANDARD_JOINER = Joiner::on(', ')->useForNull('null');
    }

    /**
     * @return Joiner
     */
    public static function standardJoiner() : Joiner
    {
        return self::$STANDARD_JOINER;
    }

    /**
     * Creates a comparator function based on the given {@link Comparator} thus it can be passed
     * to any native sorting function like {@link usort}.
     *
     * @param Comparator $comparator
     * @return callable
     */
    public static function compareFunctionFor(Comparator $comparator) : callable
    {
        return function ($object1, $object2) use ($comparator) {
            return $comparator->compare($object1, $object2);
        };
    }

    /**
     * Creates a {@link Comparator} from the given function.
     * The function will get two parameters are have to return a negative, 0, or positive
     * number depending on how the two parameters are compared to each other.
     *
     * @param callable $comparator
     * @return Comparator
     */
    public static function comparatorFrom(callable $comparator) : Comparator
    {
        return new CallableBasedComparator($comparator);
    }

    /**
     * Sorts the specified object according to the order induced by the specified comparator.
     * All elements in the object must be mutually comparable using the specified comparator
     * (that is, c.compare(e1, e2) must not throw a ClassCastException for any elements e1 and e2 in the object).
     *
     *

     * If the specified comparator is null, this method sorts the specified {@link ArrayObject} into ascending order,
     * according to the natural ordering of its elements. All elements in the object must implement
     * the {@link Comparable} interface. Furthermore, all elements in the object must be mutually comparable
     * (that is, e1.compareTo(e2) must not throw a ClassCastException for any elements e1 and e2 in the object).
     *
     * @param ArrayObject $arrayObject
     * @param Comparator $comparator
     */
    public static function sort(ArrayObject $arrayObject, Comparator $comparator = null) : void
    {
        if ($comparator === null) {
            $comparator = ComparableComparator::instance();
        }
        $arrayObject->uasort(self::compareFunctionFor($comparator));
    }

    /**
     * Returns a comparator that imposes the reverse ordering of the specified comparator.
     *
     *

     * If the specified comparator is null, it returns a comparator that imposes the reverse of the natural ordering
     * on a collection of objects that implement the {@link Comparable} interface.
     * (The natural ordering is the ordering imposed by the objects' own compareTo method.)
     *
     *

     * This enables a simple idiom for sorting (or maintaining) collections (or arrays) of objects
     * that implement the {@link Comparable} interface in reverse-natural-order.
     * For example, suppose a is an array. Then:
     *
     *

     *   Arrays::sortWith(a, Collections::reverseOrder());
     *

     *
     * sorts the array in reverse order.
     *
     * @param Comparator $comparator
     * @return Comparator
     */
    public static function reverseOrder(Comparator $comparator = null) : Comparator
    {
        if ($comparator === null) {
            $comparator = ComparableComparator::instance();
        }
        return new ReverseComparator($comparator);
    }

    /**
     * Creates an {@link SplHeap} based on the given comparator object.
     * All elements in the heap must be mutually comparable using the specified comparator
     * (that is, c.compare(e1, e2) must not throw a ClassCastException for any elements e1 and e2 in the heap).
     *
     *

     * If the specified comparator is null, all elements in the heap must implement
     * the {@link Comparable} interface. Furthermore, all elements in the heap must be mutually comparable
     * (that is, e1.compareTo(e2) must not throw a ClassCastException for any elements e1 and e2 in the heap).
     *
     * @param Comparator $comparator
     * @return SplHeap
     */
    public static function createHeap(Comparator $comparator = null) : SplHeap
    {
        if ($comparator === null) {
            $comparator = ComparableComparator::instance();
        }
        return new ComparatorBasedHeap($comparator);
    }

    /**
     * @return \Iterator
     */
    public static function emptyIterator() : \Iterator
    {
        return new EmptyIterator();
    }
}

/**
 * It is not intended to be used in your code.
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
final class ComparatorBasedHeap extends SplHeap
{
    /**
     * @var Comparator
     */
    private $comparator;

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

    /**
     * (PHP 5 >= 5.3.0)

     * Compare elements in order to place them correctly in the heap while sifting up.
     * @link http://php.net/manual/en/splheap.compare.php
     * @param mixed $value1

     * The value of the first node being compared.
     *

     * @param mixed $value2

     * The value of the second node being compared.
     *

     * @return int Result of the comparison, positive integer if value1 is greater than value2, 0 if they are equal, negative integer otherwise.
     *

     *

     * Having multiple elements with the same value in a Heap is not recommended. They will end up in an arbitrary relative position.
     */
    protected function compare($value1, $value2) : int
    {
        return $this->comparator->compare($value1, $value2);
    }
}

/**
 * It is not intended to be used in your code.
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
final class ReverseComparator implements Comparator
{
    /**
     * @var Comparator
     */
    private $comparator;

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

    /**
     * @param $object1
     * @param $object2
     * @return int a negative integer, zero, or a positive integer
     *         as the first argument is less than, equal to, or greater than the second.
     * @throws ClassCastException - if the arguments' types prevent them from being compared by this comparator.
     */
    public function compare($object1, $object2) : int
    {
        return $this->comparator->compare($object2, $object1);
    }
}

/**
 * It is not intended to be used in your code.
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
final class CallableBasedComparator implements Comparator
{
    /**
     * @var callable
     */
    private $callable;

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

    /**
     * @param $object1
     * @param $object2
     * @return int a negative integer, zero, or a positive integer
     *         as the first argument is less than, equal to, or greater than the second.
     * @throws ClassCastException - if the arguments' types prevent them from being compared by this comparator.
     */
    public function compare($object1, $object2) : int
    {
        return Functions::call($this->callable, $object1, $object2);
    }
}
Collections::init();


<?php
declare(strict_types=1);

namespace precore\util;

use Exception;
use precore\lang\NullPointerException;
use precore\lang\BaseObject;
use precore\lang\ObjectInterface;

/**
 * Class TryTo
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
abstract class TryTo extends BaseObject
{
    /**
     * Builder method for a try-catch-finally definition.
     *
     * @param Exception[] $exceptions should be caught, all exceptions will be handled if empty
     * @return TryCatch
     */
    public static function catchExceptions(array $exceptions = []) : TryCatch
    {
        return new TryCatch($exceptions);
    }

    /**
     * Executes the given function within a try block, catches all exception types given and runs the finally block.
     *
     * @param callable $tryBlock
     * @param Exception[] $exceptions should be caught, all exceptions will be handled if empty
     * @param callable $finallyBlock
     * @return TryTo
     * @throws Exception the thrown exception if it is not handled by this TryTo
     */
    public static function run(callable $tryBlock, array $exceptions = [], callable $finallyBlock = null) : TryTo
    {
        try {
            return Success::of(Functions::call($tryBlock));
        } catch (Exception $e) {
            if (count($exceptions) === 0) {
                return Failure::of($e);
            }
            $error = FluentIterable::of($exceptions)
                ->filter(Predicates::assignableFrom(get_class($e)))
                ->first();
            if ($error->isPresent()) {
                return Failure::of($e);
            }
            throw $e;
        } finally {
            if ($finallyBlock !== null) {
                Functions::call($finallyBlock);
            }
        }
    }

    /**
     * @param string $exceptionClass
     * @param callable|null $consumer
     * @return TryTo
     */
    public abstract function onFail($exceptionClass = '\Exception', callable $consumer = null) : TryTo;

    /**
     * Map success value. Do nothing if Failure (return this).
     *
     * @param callable|null $mapper
     * @return TryTo
     */
    public abstract function map(callable $mapper = null) : TryTo;

    /**
     * True if Failure / false if Success.
     *
     * @return boolean
     */
    public abstract function isFailure() : bool;

    /**
     * True if Success / false if Failure.
     *
     * @return boolean
     */
    public abstract function isSuccess() : bool;

    /**
     * Optional present if Success, Optional empty if failure
     *
     * @return Optional
     */
    public abstract function toOptional() : Optional;

    /**
     * Optional present if Failure (with Exception), Optional absent if Success.
     *
     * @return Optional
     */
    public abstract function toFailedOptional() : Optional;

    /**
     * Convert a Success to a Failure (with a null value for Exception) if predicate does not hold.
     * Do nothing to a Failure.
     *
     * @param callable|null $predicate
     * @return Optional
     * @throw NullPointerException if Success and predicate is null
     */
    public abstract function filter(callable $predicate = null) : Optional;

    /**
     * Get the contained value.
     *
     * @return mixed value
     * @throws Exception the contained exception if it is a Failure
     */
    public abstract function get();

    /**
     * Return value supplied if Failure, otherwise return Success value.
     *
     * @param $defaultValue
     * @return mixed
     */
    public abstract function orElse($defaultValue);

    /**
     * Return the value if Success, otherwise invoke supplier and return the result of that invocation.
     *
     * @param callable|null $supplier
     * @return mixed
     * @throws NullPointerException if Failure and supplier is null
     */
    public abstract function orElseGet(callable $supplier = null);

    /**
     * Recovery function - map from a failure to a Success.
     *
     * @param callable|null $recoverFunction
     * @return Success
     */
    public abstract function recover(callable $recoverFunction = null);

    /**
     * Recover if exception is of specified type.
     *
     * @param string $exceptionClass
     * @param callable|null $recoverFunction
     * @return TryTo
     */
    public abstract function recoverFor($exceptionClass = '\Exception', callable $recoverFunction = null) : TryTo;

    /**
     * If a value is present, apply the provided mapping function to it, return that result,
     * otherwise return a Failure.
     *
     * @param callable|null $mapper
     * @return TryTo
     * @throws NullPointerException if Success and mapper is null
     */
    public abstract function flatMap(callable $mapper = null) : TryTo;

    /**
     * Throw exception if Failure, do nothing if success.
     *
     * @return void
     * @throws \Exception
     */
    public abstract function throwException() : void;

    /**
     * Flatten a nested TryTo Structure.
     *
     * @return TryTo
     */
    public abstract function flatten() : TryTo;
}

final class Success extends TryTo
{
    private $value;

    private function __construct($value)
    {
        $this->value = $value;
    }

    public static function of($value)
    {
        return new Success($value);
    }

    public function onFail($exceptionClass = '\Exception', callable $consumer = null) : TryTo
    {
        return $this;
    }

    public function map(callable $mapper = null) : TryTo
    {
        return Success::of(Functions::call(Preconditions::checkNotNull($mapper), $this->value));
    }

    public function get()
    {
        return $this->value;
    }

    public function isFailure() : bool
    {
        return false;
    }

    public function isSuccess() : bool
    {
        return true;
    }

    public function toOptional() : Optional
    {
        return Optional::of($this->value);
    }

    public function filter(callable $predicate = null) : Optional
    {
        return Predicates::call(Preconditions::checkNotNull($predicate), $this->value)
            ? Optional::of($this->value)
            : Optional::absent();
    }

    public function orElse($defaultValue)
    {
        return $this->value;
    }

    public function recover(callable $recoverFunction = null)
    {
        return $this;
    }

    public function orElseGet(callable $supplier = null)
    {
        return $this->value;
    }

    public function flatMap(callable $mapper = null) : TryTo
    {
        $result = Functions::call(Preconditions::checkNotNull($mapper), $this->value);
        Preconditions::checkState($result instanceof TryTo, "result of the mapper must be a TryTo");
        return $result;
    }

    public function recoverFor($exceptionClass = '\Exception', callable $recoverFunction = null) : TryTo
    {
        return $this;
    }

    public function throwException() : void
    {
    }

    public function equals(ObjectInterface $object = null) : bool
    {
        return $object instanceof Success && $object->value === $this->value;
    }

    public function toString() : string
    {
        return Objects::toStringHelper($this)->add($this->value)->toString();
    }

    public function flatten() : TryTo
    {
        return $this->value instanceof TryTo
            ? $this->value->flatten()
            : $this;
    }

    public function toFailedOptional() : Optional
    {
        return Optional::absent();
    }
}

final class Failure extends TryTo
{
    private $exception;

    public static function of(Exception $exception)
    {
        $failure = new Failure();
        $failure->exception = $exception;
        return $failure;
    }

    public function onFail($exceptionClass = '\Exception', callable $consumer = null) : TryTo
    {
        if (is_a($this->exception, $exceptionClass)) {
            Functions::call(Preconditions::checkNotNull($consumer), $this->exception);
        }
        return $this;
    }

    public function map(callable $mapper = null) : TryTo
    {
        return $this;
    }

    public function get()
    {
        throw $this->exception;
    }

    public function isFailure() : bool
    {
        return true;
    }

    public function isSuccess() : bool
    {
        return false;
    }

    public function toOptional() : Optional
    {
        return Optional::absent();
    }

    public function filter(callable $predicate = null) : Optional
    {
        return Optional::absent();
    }

    public function orElse($defaultValue)
    {
        return $defaultValue;
    }

    public function recover(callable $recoverFunction = null)
    {
        return Success::of(Functions::call(Preconditions::checkNotNull($recoverFunction), $this->exception));
    }

    public function orElseGet(callable $supplier = null)
    {
        return Functions::call(Preconditions::checkNotNull($supplier));
    }

    public function flatMap(callable $mapper = null) : TryTo
    {
        return $this;
    }

    public function recoverFor($exceptionClass = '\Exception', callable $recoverFunction = null) : TryTo
    {
        return is_a($this->exception, $exceptionClass)
            ? Success::of(Functions::call(Preconditions::checkNotNull($recoverFunction), $this->exception))
            : $this;
    }

    public function throwException() : void
    {
        throw $this->exception;
    }

    public function equals(ObjectInterface $object = null) : bool
    {
        return $object instanceof Failure && $this->exception === $object->exception;
    }

    public function toString() : string
    {
        return Objects::toStringHelper($this)->add($this->exception)->toString();
    }

    public function flatten() : TryTo
    {
        return $this;
    }

    public function toFailedOptional() : Optional
    {
        return Optional::of($this->exception);
    }
}

final class Init
{
    private $exceptions;

    public function __construct(array $exceptions)
    {
        $this->exceptions = $exceptions;
    }

    /**
     * @param callable $supplier The function you would like to run in try
     * @return TryTo
     * @throws Exception the thrown exception if it is not handled by this TryTo
     */
    public function run(callable $supplier) : TryTo
    {
        return TryTo::run($supplier, $this->exceptions);
    }

    /**
     * @return TryCatch
     */
    public function init() : TryCatch
    {
        return new TryCatch($this->exceptions);
    }
}

final class TryCatch
{
    private $exceptions;

    public function __construct(array $exceptions)
    {
        $this->exceptions = $exceptions;
    }

    /**
     * Runs the given function within a try block.
     *
     * @param callable $tryBlock The function you would like to run in try
     * @return TryTo
     * @throws Exception the thrown exception if it is not handled by this TryTo
     */
    public function run(callable $tryBlock) : TryTo
    {
        return TryTo::run($tryBlock, $this->exceptions);
    }

    /**
     * Builder method, the given function will be executed within a try block.
     *
     * @param callable $tryBlock
     * @return AndFinally
     */
    public function whenRun(callable $tryBlock) : AndFinally
    {
        return new AndFinally($this->exceptions, $tryBlock);
    }
}

final class AndFinally
{
    private $exceptions;
    private $tryBlock;

    public function __construct(array $exceptions, callable $tryBlock)
    {
        $this->exceptions = $exceptions;
        $this->tryBlock = $tryBlock;
    }

    /**
     * Executes the try block provided before and
     * the given function is executed within a finally block.
     *
     * @param callable $finallyBlock
     * @return TryTo
     * @throws Exception the thrown exception if it is not handled by this TryTo
     */
    public function andFinally(callable $finallyBlock) : TryTo
    {
        return TryTo::run($this->tryBlock, $this->exceptions, $finallyBlock);
    }
}


<?php
/**
 * @link https://github.com/LAV45/yii2-settings
 * @copyright Copyright (c) 2016 LAV45
 * @author Alexey Loban <lav451@gmail.com>
 * @license http://opensource.org/licenses/BSD-3-Clause
 */

namespace lav45\settings\storage;

use Yii;
use yii\base\BaseObject;
use yii\helpers\FileHelper;

/**
 * Class FileStorage
 * @package lav45\settings\storage
 */
class FileStorage extends BaseObject implements StorageInterface
{
    /**
     * @var string the directory to store settings files. You may use path alias here.
     * If not set, it will use the "settings" subdirectory under the application runtime path.
     */
    public $path = '@runtime/settings';
    /**
     * @var int the permission to be set for newly created cache files.
     * This value will be used by PHP chmod() function. No umask will be applied.
     * If not set, the permission will be determined by the current environment.
     */
    public $fileMode;
    /**
     * @var int the permission to be set for newly created directories.
     * This value will be used by PHP chmod() function. No umask will be applied.
     * Defaults to 0775, meaning the directory is read-writable by owner, but read-only for other users.
     */
    public $dirMode = 0755;
    /**
     * @var string settings file suffix. Defaults to '.bin'.
     */
    public $fileSuffix = '.bin';

    /**
     * Initializes this component by ensuring the existence of the settings path.
     */
    public function init()
    {
        parent::init();
        $this->path = Yii::getAlias($this->path);
        FileHelper::createDirectory($this->path, $this->dirMode, true);
    }

    /**
     * @param string $key
     * @return false|null|string
     */
    public function getValue($key)
    {
        $file = $this->getFile($key);

        $fp = @fopen($file, 'rb');
        if ($fp !== false) {
            @flock($fp, LOCK_SH);
            $value = @stream_get_contents($fp);
            @flock($fp, LOCK_UN);
            @fclose($fp);
            return $value;
        }

        return false;
    }

    /**
     * @param string $key
     * @param string $value
     * @return bool
     */
    public function setValue($key, $value)
    {
        $file = $this->getFile($key);

        if (@file_put_contents($file, $value, LOCK_EX) !== false) {
            if ($this->fileMode !== null) {
                @chmod($file, $this->fileMode);
            }
            return true;
        }
        $error = error_get_last();
        Yii::warning("Unable to write file '{$file}': {$error['message']}", __METHOD__);
        return false;
    }

    /**
     * @param string $key
     * @return bool
     */
    public function deleteValue($key)
    {
        return @unlink($this->getFile($key));
    }

    /**
     * Returns the storage file path given the key.
     * @param string $key
     * @return string the file path
     */
    protected function getFile($key)
    {
        return $this->path . DIRECTORY_SEPARATOR . $key . $this->fileSuffix;
    }
}


<?php
declare(strict_types=1);

namespace precore\util\concurrent;

use Exception;
use precore\lang\BaseObject;
use precore\lang\RunException;
use precore\lang\Runnable;

/**
 * Useful for cron jobs. The wrapped Runnable object
 * cannot be executed more than once at the same time
 * (if you use the same lock).
 *
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
class LockedRunnableWrapper extends BaseObject implements Runnable
{
    /**
     * @var Lock
     */
    private $lock;

    /**
     * @var Runnable
     */
    private $runnable;

    /**
     * @param Runnable $runnable
     * @param Lock $lock
     */
    public function __construct(Runnable $runnable, Lock $lock)
    {
        $this->runnable = $runnable;
        $this->lock = $lock;
    }

    /**
     * It wraps any exceptions coming from inner Runnable object.
     * After the execution the lock become unlocked!
     *
     * @throws RunException
     */
    public function run() : void
    {
        $thrownException = null;
        try {
            $this->lock->lock();
            try {
                $this->runnable->run();
            } catch (Exception $e) {
                self::getLogger()->error($e);
                $thrownException = $e;
            }
            $this->lock->unLock();
        } catch (LockException $e) {
            throw new RunException('Lock error during running.', 0, $e);
        }
        if ($thrownException !== null) {
            throw new RunException('Error during execution wrapped Runnable object.', 0, $thrownException);
        }
    }
}


<?php

namespace TafDecoder\ChunkDecoder;

use TafDecoder\Entity\SurfaceWind;
use TafDecoder\Entity\Visibility;
use TafDecoder\Exception\ChunkDecoderException;
use TafDecoder\Entity\DecodedTaf;
use TafDecoder\Entity\Evolution;
use TafDecoder\Entity\CloudLayer;
use TafDecoder\Entity\WeatherPhenomenon;
use TafDecoder\Entity\Temperature;

/**
 * Chunk decoder for weather evolutions
 */
class EvolutionChunkDecoder extends TafChunkDecoder implements TafChunkDecoderInterface
{
    private $decoder_chain;

    private $strict;

    private $with_cavok;

    private $remaining;

    public function __construct($strict, $with_cavok)
    {
        $this->strict = $strict;
        $this->with_cavok = $with_cavok;
        $this->decoder_chain = array(
            new SurfaceWindChunkDecoder(),
            new VisibilityChunkDecoder(),
            new WeatherChunkDecoder(),
            new CloudChunkDecoder(),
            new TemperatureChunkDecoder(),
        );
    }

    public function setStrict($strict)
    {
        $this->strict = $strict;
    }

    public function getRemaining()
    {
        return $this->remaining;
    }

    public function getRegexp()
    {
        $type = '(BECMG\s+|TEMPO\s+|FM|PROB[034]{2}\s+){1}';
        $period = '([0-9]{4}/[0-9]{4}\s+|[0-9]{6}\s+){1}';
        $rest = '(.*)';

        return "#$type$period$rest#";
    }

    /**
     * @param string $remaining_taf
     * @param DecodedTaf $decoded_taf
     * @return string
     */
    public function parse($remaining_taf, $decoded_taf)
    {
        $result = $this->consume($remaining_taf);
        $found = $result['found'];
        $evo_type = trim($found[1]);
        $evo_period = trim($found[2]);
        $remaining = $found[3];

        if ($found == null) {
            // the first chunk didn't match anything, so we remove it to avoid an infinite loop
            $this->remaining = preg_replace('#(\S+\s+)(.*)#', '', $remaining_taf);

            return;
        }

        $evolution = new Evolution();
        $evolution->setType($evo_type);
        if (strpos($result['remaining'], 'PROB') !== false) {
            // if the line started with PROBnn it won't have been consumed and we'll find it in $remaining
            $evolution->setProbability(trim($result['remaining']));
        }

        // period
        if ($evo_type == 'BECMG' || $evo_type == 'TEMPO') {
            $periodArr = explode('/', $evo_period);
            $evolution->setFromDay(intval(mb_substr($periodArr[0], 0, 2)));
            $evolution->setFromTime(mb_substr($periodArr[0], 2, 2).':00 UTC');
            $evolution->setToDay(intval(mb_substr($periodArr[1], 0, 2)));
            $evolution->setToTime(mb_substr($periodArr[1], 2, 2).':00 UTC');
        } else {
            $evolution->setFromDay(intval(mb_substr($evo_period, 0, 2)));
            $evolution->setFromTime(mb_substr($evo_period, 2, 2).':'.mb_substr($evo_period, 4, 2).' UTC');
        }

        // rest
        $remaining = $this->parseEntitiesChunk($evolution, $remaining, $decoded_taf);

        $this->remaining = $remaining;
    }

    /**
     * Extract the weather elements (surface winds, visibility, etc) between 2 evolution tags (BECMG, TEMPO or FM)
     *
     * @param Evolution $evolution
     * @param string $chunk
     * @param DecodedTaf $decoded_taf
     * @throws ChunkDecoderException
     * @return string
     */
    private function parseEntitiesChunk($evolution, $chunk, $decoded_taf)
    {
        // For each value we detect, we'll clone the $evolution object, complete the clone,
        // and add it to the corresponding entity of the decoded taf

        $remaining_evo = $chunk;
        $tries = 0;

        // call each decoder in the chain and use results to populate the decoded taf
        foreach ($this->decoder_chain as $chunk_decoder) {
            try {
                // we check for probability in each loop, as it can be anywhere
                $remaining_evo = $this->probabilityChunkDecoder($evolution, $remaining_evo, $decoded_taf);

                // reset cavok
                $this->with_cavok = false;

                // try to parse the chunk with the current chunk decoder
                $decoded = $chunk_decoder->parse($remaining_evo, $this->with_cavok);

                // map the obtained fields (if any) to a original entity in the decoded_taf
                $result = $decoded['result'];
                $entity_name = current(array_keys($result));
                if ($entity_name == 'cavok') {
                    if ($result[$entity_name]) {
                        $this->with_cavok = true;
                    }
                    $entity_name = 'visibility';
                }
                $entity = $result[$entity_name];
                if ($entity == null && $entity_name != 'visibility') {
                    // visibility will be null if cavok is true but we still want to add the evolution
                    throw new ChunkDecoderException(
                        $chunk,
                        $remaining_evo,
                        'Bad format for weather evolution',
                        $this
                    );
                }
                if ($entity_name == 'maxTemperature') {
                    $this->addEvolution($decoded_taf, $evolution, $result, 'maxTemperature');
                    $this->addEvolution($decoded_taf, $evolution, $result, 'minTemperature');
                } else {
                    $this->addEvolution($decoded_taf, $evolution, $result, $entity_name);
                }

                // update remaining evo for the next round
                $remaining_evo = $decoded['remaining_taf'];

            } catch (ChunkDecoderException $e) {
                if (++$tries == count($this->decoder_chain)) {
                    if ($this->strict) {
                        throw new ChunkDecoderException(
                            $chunk,
                            $remaining_evo,
                            'Bad format for evolution information',
                            $this
                        );
                    } else {
                        // we tried all the chunk decoders on the first chunk and none of them got a match,
                        // so we drop it
                        $remaining_evo = preg_replace('#(\S+\s+)#', '', $remaining_evo);
                    }
                }
            }
        }

        return $remaining_evo;
    }

    /**
     * Look recursively for probability (PROBnn) attributes and embed a new evolution object one level deeper for each
     *
     * @param Evolution $evolution
     * @param string $chunk
     * @param DecodedTaf $decoded_taf
     * @return string
     */
    private function probabilityChunkDecoder($evolution, $chunk, $decoded_taf)
    {
        $regexp = '#^(PROB[034]{2}\s+){1}(TEMPO\s+){0,1}([0-9]{4}/[0-9]{4}){0,1}(.*)#';

        if (preg_match($regexp, $chunk, $matches)) {
            $found = $matches;
        } else {
            return $chunk;
        }

        $prob = trim($found[1]);
        $type = trim($found[2]);
        $period = trim($found[3]);
        $remaining = trim($found[4]);

        if (strpos($prob, 'PROB') !== false) {
            $evolution->setProbability($prob);
            $embeddedEvolution = new Evolution();
            if ($type) {
                $embeddedEvolution->setType($type);
            } else {
                $embeddedEvolution->setType('probability');
            }
            $periodArr = explode('/', $period);
            $embeddedEvolution->setFromDay(intval(mb_substr($periodArr[0], 0, 2)));
            $embeddedEvolution->setFromTime(mb_substr($periodArr[0], 2, 2).':00 UTC');
            $embeddedEvolution->setToDay(intval(mb_substr($periodArr[1], 0, 2)));
            $embeddedEvolution->setToTime(mb_substr($periodArr[1], 2, 2).':00 UTC');

            $evolution->addEvolution($embeddedEvolution);
            // recurse on the remaining chunk to extract the weather elements it contains
            $chunk = $this->parseEntitiesChunk($evolution, $remaining, $decoded_taf);
        }

        return $chunk;
    }

    /**
     * Add the evolution to the decodedTaf's entity
     *
     * @param DecodedTaf $decoded_taf
     * @param Evolution $evolution
     * @param array $result
     * @param string $entity_name
     */
    private function addEvolution($decoded_taf, $evolution, $result, $entity_name)
    {
        // clone the evolution entity
        /** @var Evolution $newEvolution */
        $new_evolution = clone($evolution);

        // add the new entity to it
        $new_evolution->setEntity($result[$entity_name]);

        // possibly add cavok to it
        if ($entity_name == 'visibility' && $this->with_cavok == true) {
            $new_evolution->setCavok(true);
        }

        // get the original entity from the decoded taf or a new one decoded taf doesn't contain it yet
        $getter_name = 'get'.ucfirst($entity_name);
        $setter_name = 'set'.ucfirst($entity_name);
        $decoded_entity = $decoded_taf->$getter_name();
        if ($decoded_entity == null || $entity_name == 'clouds' || $entity_name == 'weatherPhenomenons') {
            // that entity is not in the decoded_taf yet, or it's a cloud layer which is a special case
            $decoded_entity = $this->instantiateEntity($entity_name);
        }

        // add the new evolution to that entity
        $decoded_entity->addEvolution($new_evolution);

        // update the decoded taf's entity or add the new one to it
        if ($entity_name == 'clouds') {
            $decoded_taf->addCloud($decoded_entity);
        } elseif ($entity_name == 'weatherPhenomenons') {
            $decoded_taf->addWeatherPhenomenon($decoded_entity);
        } else {
            $decoded_taf->$setter_name($decoded_entity);
        }
    }

    /**
     * Instantiate a new entity when an evolution needs one that's not present in decodedTaf already
     *
     * @param $entity_name
     * @return mixed
     */
    private function instantiateEntity($entity_name)
    {
        $entity = null;

        if ($entity_name == 'weatherPhenomenons') {
            $entity = new WeatherPhenomenon();
        } else if ($entity_name == 'maxTemperature') {
            $entity =  new Temperature();
        } else if ($entity_name == 'minTemperature') {
            $entity = new Temperature();
        } else if ($entity_name == 'clouds') {
            $entity = new CloudLayer();
        } else if ($entity_name == 'surfaceWind') {
            $entity = new SurfaceWind();
        } else if ($entity_name = 'visibility') {
            $entity = new Visibility();
        }

        return $entity;
    }
}


<?php
declare(strict_types=1);

namespace precore\lang;

use LazyMap\CallbackLazyMap;
use precore\util\Preconditions;
use ReflectionClass;
use RuntimeException;

/**
 * Extends ReflectionClass with some new features.
 * It caches the instances if you obtain them through ObjectClass::forName().
 *
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
class ObjectClass extends ReflectionClass
{
    /**
     * @var CallbackLazyMap
     */
    private static $classMap;

    /**
     * Do not call from your code, workaround to initialize static variable.
     */
    public static function init() : void
    {
        self::$classMap = new CallbackLazyMap(
            function ($className) {
                $trimmedClassName = trim($className, '\\');
                return $trimmedClassName === $className
                    ? new ObjectClass($className)
                    : ObjectClass::$classMap->$trimmedClassName;
            }
        );
    }

    /**
     * @param string $className FQCN
     * @return ObjectClass
     */
    public static function forName($className) : ObjectClass
    {
        return self::$classMap->$className;
    }

    protected function getSlashedFileName()
    {
        $classFileName = $this->getFileName();
        Preconditions::checkState($classFileName !== false, 'This method cannot be called for built-in classes!');
        return str_replace('\\', '/', $classFileName);
    }

    protected function getSlashedName()
    {
        return str_replace('\\', '/', $this->getName());
    }

    /**
     * Whether the class name is PSR-0 compatible or not.
     *
     * @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
     * @return boolean
     */
    public function isPsr0Compatible() : bool
    {
        return preg_match("#{$this->getSlashedName()}.php$#", $this->getSlashedFileName()) !== 0;
    }

    /**
     * Get a file name depending on current class.
     *
     * Suppose the class is \foo\Bar which located in src/foo/Bar.php
     * Absolute path: /resources/res1 must be located in src/resources/res1
     * Relative path: resources/res2 must be located in src/foo/resources/res2
     *
     * @param string $resource
     * @return string File path of $resource or null if not exists
     * @see java.lang.Class
     * @throws RuntimeException Class is built-int
     * @throws IllegalStateException Class is not PSR-0 compatible
     */
    public function getResource($resource) : ?string
    {
        Preconditions::checkState($this->isPsr0Compatible(), "Class '%s' must be PSR-0 compatible!", $this->getName());
        $slashedFileName = $this->getSlashedFileName();
        $filePath = $resource[0] == '/'
            ? str_replace("/{$this->getSlashedName()}.php", '', $slashedFileName) . $resource
            : dirname($slashedFileName) . '/' . $resource;
        return is_file($filePath) ? $filePath : null;
    }

    /**
     * Check whether $object can be cast to $this->getName().
     * Note, that null can be cast to anything. In other words, it returns null if the given object is null.
     *
     * @param object $object
     * @return object $object itself
     * @throws ClassCastException
     */
    public function cast($object)
    {
        if ($object !== null && !$this->isInstance($object)) {
            $objectClass = get_class($object);
            throw new ClassCastException("'{$objectClass}' cannot be cast to '{$this->getName()}'");
        }
        return $object;
    }

    /**
     * Determines if the class or interface represented by this ObjectClass object is either the same as,
     * or is a superclass or superinterface of, the class or interface
     * represented by the specified ObjectClass parameter. It returns true if so; otherwise it returns false.
     *
     * @param ReflectionClass $class
     * @return bool
     */
    public function isAssignableFrom(ReflectionClass $class) : bool
    {
        return $this->getName() == $class->getName() || $class->isSubclassOf($this->getName());
    }
}
ObjectClass::init();


<?php
declare(strict_types=1);

namespace precore\util\error;

/**
 * Helper class to be able to catch errors as exceptions.
 *
 * @package precore\util\error
 *
 * @author Janos Szurovecz <szjani@szjani.hu>
 * @see http://hu1.php.net/manual/en/function.set-error-handler.php#112881
 */
final class ErrorHandler
{
    private function __construct()
    {
    }

    /**
     * Converts all errors to the proper exception.
     */
    public static function register()
    {
        set_error_handler(
            function ($code, $message, $file, $line, $context) {
                if (error_reporting() == 0) {
                    return false;
                }
                ErrorType::forCode($code)->throwException($message, $file, $line, $context);
            }
        );
    }
}


<?php

namespace TafDecoder\Entity;

class ForecastPeriod
{
    // starting day of forecast period
    private $from_day;

    // starting hour of forecast period
    private $from_hour;

    // ending day of forecast period
    private $to_day;

    // ending hour of forecast period
    private $to_hour;

    /**
     * Check if the forecast period is valid
     */
    public function isValid()
    {
        // check that attribute aren't null
        if (
            $this->getFromDay() == null || $this->getFromHour() == null ||
            $this->getToDay() == null || $this->getToHour() == null
        ) {
            return false;
        }

        // check ranges
        if ($this->getFromDay()  < 1 || $this->getFromDay() > 31) {
            return false;
        }
        if ($this->getToDay() < 1 || $this->getToDay() > 31) {
            return false;
        }
        if ($this->getFromHour() > 24 || $this->getToHour() > 24) {
            return false;
        }

        if ($this->getFromDay() == $this->getToDay() && $this->getFromHour() >= $this->getToHour()) {
            return false;
        }

        return true;
    }

    public function setFromDay($from_day)
    {
        $this->from_day = $from_day;

        return $this;
    }

    public function getFromDay()
    {
        return $this->from_day;
    }

    public function setFromHour($from_hour)
    {
        $this->from_hour = $from_hour;

        return $this;
    }

    public function getFromHour()
    {
        return $this->from_hour;
    }

    public function setToDay($to_day)
    {
        $this->to_day = $to_day;

        return $this;
    }

    public function getToDay()
    {
        return $this->to_day;
    }

    public function setToHour($to_hour)
    {
        $this->to_hour = $to_hour;

        return $this;
    }

    public function getToHour()
    {
        return $this->to_hour;
    }

}


<?php
declare(strict_types=1);

namespace precore\util;

use DateTime;
use ErrorException;
use precore\lang\BaseObject;

/**
 * Can be used in ObjectInterface implementations for overriding toString() method.
 *
 * @package precore\util
 *
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
class ToStringHelper extends BaseObject
{
    private $className;

    /**
     * @var ToStringHelperItem[]
     */
    private $values = [];

    private $omitNullValues = false;

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

    public static function valueToString($value) : string
    {
        $stringValue = null;
        if ($value === null) {
            $stringValue = 'null';
        } elseif ($value instanceof DateTime) {
            $stringValue = $value->format(DateTime::ISO8601);
        } elseif (is_array($value)) {
            $parts = [];
            foreach ($value as $key => $valueItem) {
                $parts[] = $key . '=' . self::valueToString($valueItem);
            }
            $stringValue = sprintf('[%s]', implode(', ', $parts));
        } else {
            try {
                $stringValue = (string) $value;
            } catch (ErrorException $e) {
                $stringValue = spl_object_hash($value);
            }
        }
        return $stringValue;
    }

    /**
     * Can be called with 1 or 2 parameters. In the first case only the value,
     * otherwise a key/value pair is passed.
     *
     * @param mixed[] $param
     * @return ToStringHelper $this
     * @throws \InvalidArgumentException if the number of parameters is not 1 or 2
     */
    public function add(...$param) : ToStringHelper
    {
        $count = count($param);
        Preconditions::checkArgument($count == 1 || $count == 2);
        $this->values[] = $count == 2
            ? new ToStringHelperItem($param[0], $param[1])
            : new ToStringHelperItem(null, $param[0]);
        return $this;
    }

    /**
     * Configures the ToStringHelper so toString() will ignore properties with null value.
     *
     * @return ToStringHelper $this
     */
    public function omitNullValues() : ToStringHelper
    {
        $this->omitNullValues = true;
        return $this;
    }

    public function toString() : string
    {
        return sprintf("%s%s", $this->className, $this->membersToString());
    }

    protected function membersToString() : string
    {
        $parts = [];
        foreach ($this->values as $item) {
            if ($this->omitNullValues && $item->getValue() === null) {
                continue;
            }
            $parts[] = $item->toString();
        }
        return sprintf('{%s}', implode(', ', $parts));
    }
}


<?php
declare(strict_types=1);

namespace precore\util;

use ArrayIterator;
use BadMethodCallException;
use precore\lang\BaseObject;
use precore\lang\ObjectInterface;
use Traversable;

/**
 * An object which joins pieces of text (specified as an array or {@link Traversable}) with a separator.
 * Example:
 *

 *   $joiner = Joiner::on("; ")->skipNulls();
 *   return $joiner->join(["Harry", null, "Ron", "Hermione"]);
 *

 *
 * This returns the string "Harry; Ron; Hermione". Note that all input elements are
 * converted to strings using {@link ToStringHelper::valueToString} before being appended.
 *
 *
If neither {@link Joiner::skipNulls()} nor {@link Joiner::useForNull()} is specified, the joining
 * methods will throw {@link NullPointerException} if any given element is null.
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
abstract class Joiner extends BaseObject
{
    private $separator;

    /**
     * @param string $separator
     */
    protected function __construct(string $separator)
    {
        $this->separator = $separator;
    }

    /**
     * Returns a joiner which automatically places {@link Joiner::separator} between consecutive elements.
     *
     * @param string $separator
     * @return Joiner
     * @throws \InvalidArgumentException if separator is not a string
     */
    public static function on($separator) : Joiner
    {
        return new SimpleJoiner($separator);
    }

    /**
     * Returns a joiner with the same behavior as this joiner, except automatically skipping over any
     * provided null elements.
     *
     * @return Joiner
     */
    public function skipNulls() : Joiner
    {
        return new SkipNullJoiner($this->separator);
    }

    /**
     * Returns a joiner with the same behavior as this one, except automatically substituting $nullText
     * for any provided null elements.
     *
     * @param $nullText
     * @return Joiner
     */
    public function useForNull(string $nullText) : Joiner
    {
        return new UseForNullJoiner($this->separator, $nullText);
    }

    /**
     * @param FluentIterable $iterable
     * @return FluentIterable
     */
    protected abstract function modifyIterable(FluentIterable $iterable) : FluentIterable;

    /**
     * Returns a string containing the string representation of each element of $parts, using the previously
     * configured separator between each.
     *
     * @param array|Traversable $parts
     * @return string
     * @throws \InvalidArgumentException if $parts is not an array or a Traversable
     */
    final public function join($parts) : string
    {
        if (is_array($parts)) {
            $parts = new ArrayIterator($parts);
        }
        Preconditions::checkArgument($parts instanceof Traversable, 'parts must be an array or a Traversable');
        $iterator = $this->modifyIterable(FluentIterable::from($parts))
            ->transform(
                function ($element) {
                    return ToStringHelper::valueToString($element);
                }
            )
            ->iterator();
        $res = '';
        while ($iterator->valid()) {
            $res .= $iterator->current();
            $iterator->next();
            if ($iterator->valid()) {
                $res .= $this->separator;
            }
        }
        return $res;
    }

    public function toString() : string
    {
        return Objects::toStringHelper($this)
            ->add('separator', $this->separator)
            ->toString();
    }

    public function equals(ObjectInterface $object = null) : bool
    {
        /* @var $object Joiner */
        return $object !== null
            && $this->getClassName() === $object->getClassName()
            && $this->separator === $object->separator;
    }
}

/**
 * It is not intended to be used in your code.
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
final class SimpleJoiner extends Joiner
{
    /**
     * @param FluentIterable $iterable
     * @return FluentIterable
     */
    protected function modifyIterable(FluentIterable $iterable) : FluentIterable
    {
        return $iterable->filter(
            function ($element) {
                Preconditions::checkNotNull(
                    $element,
                    "There is a null input, consider to use skipNulls() or useForNull()"
                );
                return true;
            }
        );
    }
}

/**
 * It is not intended to be used in your code.
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
final class SkipNullJoiner extends Joiner
{
    public function skipNulls() : Joiner
    {
        throw new BadMethodCallException('already specified skipNulls');
    }

    public function useForNull(string $nullText) : Joiner
    {
        throw new BadMethodCallException('already specified skipNulls');
    }

    /**
     * @param FluentIterable $iterable
     * @return FluentIterable
     */
    protected function modifyIterable(FluentIterable $iterable) : FluentIterable
    {
        return $iterable->filter(Predicates::notNull());
    }
}

/**
 * It is not intended to be used in your code.
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
final class UseForNullJoiner extends Joiner
{
    private $useForNull;

    public function __construct(string $separator, string $nullText)
    {
        parent::__construct($separator);
        $this->useForNull = $nullText;
    }

    public function skipNulls() : Joiner
    {
        throw new BadMethodCallException('already specified useForNull');
    }

    public function useForNull(string $nullText) : Joiner
    {
        throw new BadMethodCallException('already specified useForNull');
    }

    /**
     * @param FluentIterable $iterable
     * @return FluentIterable
     */
    protected function modifyIterable(FluentIterable $iterable) : FluentIterable
    {
        return $iterable->transform(
            function ($element) {
                return $element !== null ? $element : $this->useForNull;
            }
        );
    }

    public function equals(ObjectInterface $object = null) : bool
    {
        /* @var $object UseForNullJoiner */
        return parent::equals($object) && $this->useForNull === $object->useForNull;
    }

    public function toString() : string
    {
        return Objects::toStringHelper($this)
            ->add('parent', parent::toString())
            ->add('useForNull', $this->useForNull)
            ->toString();
    }
}


<?php
declare(strict_types=1);

namespace precore\util;

use ArrayObject;
use Exception;

/**
 * Utility class for {@link Exception}s.
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
final class Exceptions
{
    private function __construct()
    {
    }

    /**
     * Gets an {@link Exception} cause chain as an {@link ArrayObject}.
     * The first entry in the list will be $exception followed by its cause hierarchy.
     *
     * @param Exception $exception
     * @return ArrayObject
     */
    public static function getCausalChain(Exception $exception) : ArrayObject
    {
        $result = new ArrayObject();
        while ($exception !== null) {
            $result[] = $exception;
            $exception = $exception->getPrevious();
        }
        return $result;
    }

    /**
     * Returns the innermost cause of $exception.
     *
     * @param Exception $exception
     * @return Exception
     */
    public static function getRootCause(Exception $exception) : Exception
    {
        $result = null;
        while ($exception !== null) {
            $result = $exception;
            $exception = $exception->getPrevious();
        }
        return $result;
    }

    /**
     * Propagates throwable exactly as-is, if and only if it is an instance of exceptionClass.
     *
     * @param Exception $exception
     * @param string $exceptionClass
     * @throws Exception the given $exception
     */
    public static function propagateIfInstanceOf(Exception $exception, string $exceptionClass) : void
    {
        if (is_a($exception, $exceptionClass)) {
            throw $exception;
        }
    }
}


<?php

namespace TafDecoder\ChunkDecoder;

abstract class TafChunkDecoder
{
    /**
     * Extract the corresponding chunk from the remaining taf
     *
     * @param string $remaining_taf
     * @return array of matches if any or null if no match, + updated remaining taf
     */
    public function consume($remaining_taf)
    {
        $chunk_regexp = $this->getRegexp();

        // try to match chunk's regexp on remaining taf
        if (preg_match($chunk_regexp, $remaining_taf, $matches)) {
            $found = $matches;
        } else {
            $found = null;
        }

        // consume what has been previously found with the same regexp
        $new_remaining_taf = preg_replace($chunk_regexp, '', $remaining_taf, 1);

        return array(
            'found' => $found,
            'remaining' => $new_remaining_taf,
        );
    }
}


<?php
declare(strict_types=1);

namespace precore\util;

use precore\lang\IllegalStateException;
use precore\lang\NullPointerException;
use precore\lang\BaseObject;
use precore\lang\ObjectInterface;

/**
 * An immutable object that may contain a non-null reference to another object. Each
 * instance of this type either contains a non-null reference, or contains nothing (in
 * which case we say that the reference is "absent"); it is never said to "contain null".
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
abstract class Optional extends BaseObject
{
    private static $ABSENT;

    public static function init() : void
    {
        self::$ABSENT = new Absent();
    }

    /**
     * Returns an {@link Optional} instance containing the given non-null reference.
     *
     * @param $instance
     * @return Optional
     * @throws NullPointerException if the given instance is null
     */
    public static function of($instance) : Optional
    {
        return new Present($instance);
    }

    /**
     * If nullableReference is non-null, returns an {@link Optional} instance containing that
     * reference; otherwise returns {@link Optional::absent}.
     *
     * @param $nullableReference
     * @return Optional
     */
    public static function ofNullable($nullableReference) : Optional
    {
        return $nullableReference === null
            ? Optional::absent()
            : Optional::of($nullableReference);
    }

    /**
     * @return Optional
     */
    public static function absent() : Optional
    {
        return self::$ABSENT;
    }

    /**
     * @return mixed the contained instance
     * @throws IllegalStateException if the instance is absent
     */
    public abstract function get();

    /**
     * Returns true if this holder contains a (non-null) instance.
     *
     * @return boolean
     */
    public abstract function isPresent() : bool;

    /**
     * If a value is present, invoke the specified consumer with the value, otherwise do nothing.
     *
     * @param callable $consumer
     * @return void
     * @throws NullPointerException if value is present and consumer is null
     */
    public abstract function ifPresent(callable $consumer = null) : void;

    /**
     * Returns the contained instance if it is present; null otherwise. If the
     * instance is known to be present, use {@link get()} instead.
     *
     * @return mixed
     */
    public abstract function orNull();

    /**
     * Returns the contained instance if it is present; defaultValue otherwise.
     *
     * @param $defaultValue
     * @return mixed
     */
    public abstract function orElse($defaultValue);

    /**
     * Return the value if present, otherwise invoke supplier and return the result of that invocation.
     *
     * @param callable $supplier
     * @return mixed
     * @throws NullPointerException if value is not present and other is null
     */
    public abstract function orElseGet(callable $supplier = null);

    /**
     * Return the contained value, if present, otherwise throw an exception to be created by the provided supplier.
     *
     * @param callable $exceptionSupplier
     * @return mixed
     * @throws \Exception provided by $exceptionSupplier
     * @throws NullPointerException if no value is present and $exceptionSupplier is null
     */
    public abstract function orElseThrow(callable $exceptionSupplier = null);

    /**
     * If a value is present, and the value matches the given predicate,
     * return an Optional describing the value, otherwise return an empty Optional.
     *
     * @param callable $predicate
     * @return Optional
     */
    public abstract function filter(callable $predicate) : Optional;

    /**
     * If a value is present, apply the provided mapping function to it,
     * and if the result is non-null, return an Optional describing the result.
     * Otherwise return an empty Optional.
     *
     * @see Optional::transformer
     * @param callable $mapper
     * @return Optional
     */
    public abstract function map(callable $mapper) : Optional;

    /**
     * If a value is present, apply the provided Optional-bearing mapping function to it, return that result,
     * otherwise return an empty Optional.
     *
     * @param callable $mapper
     * @return Optional
     */
    public abstract function flatMap(callable $mapper) : Optional;
}

/**
 * It is not intended to be used in your code.
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
final class Absent extends Optional
{
    public function get()
    {
        throw new IllegalStateException("This instance is absent");
    }

    public function isPresent() : bool
    {
        return false;
    }

    public function orNull()
    {
        return null;
    }

    public function orElse($defaultValue)
    {
        return $defaultValue;
    }

    public function equals(ObjectInterface $object = null) : bool
    {
        return $object instanceof Absent;
    }

    public function filter(callable $predicate) : Optional
    {
        return $this;
    }

    public function orElseGet(callable $supplier = null)
    {
        return Functions::call(Preconditions::checkNotNull($supplier));
    }

    public function ifPresent(callable $consumer = null) : void
    {
    }

    public function toString() : string
    {
        return $this->getClassName();
    }

    public function flatMap(callable $mapper) : Optional
    {
        return $this;
    }

    public function map(callable $mapper) : Optional
    {
        return $this;
    }

    public function orElseThrow(callable $exceptionSupplier = null) : void
    {
        $exception = Functions::call(Preconditions::checkNotNull($exceptionSupplier));
        Preconditions::checkState($exception instanceof \Exception, '$exceptionSupplier must create an Exception');
        throw $exception;
    }
}

/**
 * It is not intended to be used in your code.
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
final class Present extends Optional
{
    private $instance;

    public function __construct($instance)
    {
        $this->instance = Preconditions::checkNotNull($instance);
    }

    public function get()
    {
        return $this->instance;
    }

    public function isPresent() : bool
    {
        return true;
    }

    public function orNull()
    {
        return $this->instance;
    }

    public function orElse($defaultValue)
    {
        return $this->instance;
    }

    public function equals(ObjectInterface $object = null) : bool
    {
        return $object instanceof Present
            && Objects::equal($this->instance, $object->instance);
    }

    public function filter(callable $predicate) : Optional
    {
        return Predicates::call($predicate, $this->instance)
            ? $this
            : Optional::absent();
    }

    public function orElseGet(callable $supplier = null)
    {
        return $this->instance;
    }

    public function ifPresent(callable $consumer = null) : void
    {
        Functions::call(Preconditions::checkNotNull($consumer), $this->instance);
    }

    public function toString() : string
    {
        return Objects::toStringHelper($this)->add($this->instance)->toString();
    }

    public function flatMap(callable $mapper) : Optional
    {
        $result = Functions::call($mapper, $this->instance);
        Preconditions::checkState($result instanceof Optional, "result of the mapper must be an Optional");
        return $result;
    }

    public function map(callable $mapper) : Optional
    {
        return Optional::ofNullable(Functions::call($mapper, $this->instance));
    }

    public function orElseThrow(callable $exceptionSupplier = null)
    {
        return $this->instance;
    }
}
Optional::init();


<?php
/**
 * @link https://github.com/LAV45/yii2-settings
 * @copyright Copyright (c) 2016 LAV45
 * @author Alexey Loban <lav451@gmail.com>
 * @license http://opensource.org/licenses/BSD-3-Clause
 */

namespace lav45\settings\storage;

use yii\helpers\VarDumper;

class PhpFileStorage extends FileStorage
{
    /**
     * @var string settings file suffix.
     */
    public $fileSuffix = '.php';

    /**
     * @param string $key
     * @return mixed
     */
    public function getValue($key)
    {
        $fileName = $this->getFile($key);
        if (file_exists($fileName)) {
            return include $fileName;
        }
        return false;
    }

    /**
     * @param string $key
     * @param mixed $value
     * @return bool
     */
    public function setValue($key, $value)
    {
        $value = is_string($value) ? "'{$value}'" : VarDumper::export($value);
        $value = "<?php\nreturn {$value};\n";

        $result = parent::setValue($key, $value);

        if ($result === true) {
            $fileName = $this->getFile($key);
            $this->forceScriptCache($fileName);
        }

        return $result;
    }

    /**
     * Forcibly caches data of this file in OPCache or APC.
     * @param string $fileName file name.
     * @since 1.0.4
     */
    protected function forceScriptCache($fileName)
    {
        if (
            (PHP_SAPI !== 'cli' && ini_get('opcache.enable')) ||
            ini_get('opcache.enable_cli')
        ) {
            opcache_invalidate($fileName, true); // @codeCoverageIgnore
            opcache_compile_file($fileName); // @codeCoverageIgnore
        }
        if (ini_get('apc.enabled')) {
            apc_delete_file($fileName); // @codeCoverageIgnore
            apc_bin_loadfile($fileName); // @codeCoverageIgnore
        }
    }
}


<?php
declare(strict_types=1);

namespace precore\util;

use Countable;
use Iterator;
use IteratorAggregate;
use precore\lang\NullPointerException;
use Traversable;

/**
 * Helper class for {@link IteratorAggregate} objects.
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
final class Iterables
{
    private function __construct()
    {
    }

    /**
     * @param array $array
     * @return IteratorAggregate
     */
    public static function fromArray(array $array) : IteratorAggregate
    {
        return Iterables::from(Iterators::forArray($array));
    }

    /**
     * Creates an {@link IteratorAggregate} from traversable, regardless of its type.
     *
     * @param Traversable $traversable
     * @return IteratorAggregate
     */
    public static function from(Traversable $traversable) : IteratorAggregate
    {
        Preconditions::checkArgument($traversable instanceof Iterator || $traversable instanceof IteratorAggregate);
        return $traversable instanceof IteratorAggregate
            ? $traversable
            : new CallableIterable(
                function () use ($traversable) {
                    return $traversable;
                }
            );
    }

    /**
     * Returns the elements of unfiltered that satisfy a predicate.
     *
     * @param IteratorAggregate $unfiltered
     * @param callable $predicate
     * @return IteratorAggregate
     */
    public static function filter(IteratorAggregate $unfiltered, callable $predicate) : IteratorAggregate
    {
        return new CallableIterable(
            function () use ($unfiltered, $predicate) {
                return Iterators::filter(Iterators::from($unfiltered->getIterator()), $predicate);
            }
        );
    }

    /**
     * Returns all instances of class className in unfiltered.
     *
     * @param IteratorAggregate $unfiltered
     * @param string $className
     * @return IteratorAggregate
     */
    public static function filterBy(IteratorAggregate $unfiltered, string $className) : IteratorAggregate
    {
        return self::from(Iterators::filterBy(Iterators::from($unfiltered->getIterator()), $className));
    }

    /**
     * Combines two iterables into a single iterable.
     * The returned iterable has an iterator that traverses the elements in a,
     * followed by the elements in b. The source iterators are not polled until necessary.
     *
     * @param IteratorAggregate $a
     * @param IteratorAggregate $b
     * @return IteratorAggregate
     */
    public static function concat(IteratorAggregate $a, IteratorAggregate $b) : IteratorAggregate
    {
        return self::from(Iterators::concat(Iterators::from($a->getIterator()), Iterators::from($b->getIterator())));
    }

    /**
     * Returns the first element in iterable that satisfies the given predicate. If there is no such an element,
     * it returns $defaultValue.
     *
     * @param IteratorAggregate $iterable
     * @param callable $predicate
     * @param null $defaultValue
     * @return mixed
     */
    public static function find(IteratorAggregate $iterable, callable $predicate, $defaultValue = null)
    {
        return Iterators::find(Iterators::from($iterable->getIterator()), $predicate, $defaultValue);
    }

    /**
     * @param IteratorAggregate $iterable
     * @param callable $predicate
     * @return Optional
     * @throws NullPointerException if
     */
    public static function tryFind(IteratorAggregate $iterable, callable $predicate) : Optional
    {
        return Iterators::tryFind(Iterators::from($iterable->getIterator()), $predicate);
    }

    /**
     * Divides an iterable into unmodifiable sublists of the given size (the final iterable may be smaller).
     *
     * For example, partitioning an iterable containing [a, b, c, d, e] with a partition size
     * of 3 yields [[a, b, c], [d, e]] -- an outer iterable containing two inner lists of three and two elements,
     * all in the original order.
     *
     * @param IteratorAggregate $iterable
     * @param int $size
     * @return IteratorAggregate
     * @throws \InvalidArgumentException if $size is non positive
     */
    public static function partition(IteratorAggregate $iterable, int $size) : IteratorAggregate
    {
        return FluentIterable::from(Iterators::partition(Iterators::from($iterable->getIterator()), $size))
            ->transform(
                function (Iterator $element) {
                    return new CallableIterable(
                        function () use ($element) {
                            return $element;
                        }
                    );
                }
            );
    }

    /**
     * Divides an iterable into unmodifiable sublists of the given size,
     * padding the final iterable with null values if necessary.
     *
     * For example, partitioning an iterable containing [a, b, c, d, e] with a partition size
     * of 3 yields [[a, b, c], [d, e, null]] -- an outer iterable containing two inner lists of three elements each,
     * all in the original order.
     *
     * @param IteratorAggregate $iterable
     * @param int $size
     * @return IteratorAggregate
     * @throws \InvalidArgumentException if $size is non positive
     */
    public static function paddedPartition(IteratorAggregate $iterable, int $size) : IteratorAggregate
    {
        return FluentIterable::from(Iterators::paddedPartition(Iterators::from($iterable->getIterator()), $size))
            ->transform(
                function (Iterator $element) {
                    return new CallableIterable(
                        function () use ($element) {
                            return $element;
                        }
                    );
                }
            );
    }

    /**
     * Combines multiple iterables into a single iterable.
     * The returned iterable has an iterator that traverses the elements of each iterable in inputs.
     * The input iterators are not polled until necessary.
     *
     * @param IteratorAggregate $iterables of Traversable objects
     * @return IteratorAggregate
     */
    public static function concatIterables(IteratorAggregate $iterables) : IteratorAggregate
    {
        return self::from(
            Iterators::concatIterators(
                FluentIterable::from($iterables)
                    ->transform(
                        function (Traversable $element) {
                            return Iterators::from($element);
                        }
                    )
                    ->iterator()
            )
        );
    }

    /**
     * Returns true if any element in iterable satisfies the predicate.
     *
     * @param IteratorAggregate $iterable
     * @param callable $predicate
     * @return boolean
     */
    public static function any(IteratorAggregate $iterable, callable $predicate) : bool
    {
        return Iterators::any(Iterators::from($iterable->getIterator()), $predicate);
    }

    /**
     * Returns true if every element in iterable satisfies the predicate. If iterable is empty, true is returned.
     *
     * @param IteratorAggregate $iterable
     * @param callable $predicate
     * @return boolean
     */
    public static function all(IteratorAggregate $iterable, callable $predicate) : bool
    {
        return Iterators::all(Iterators::from($iterable->getIterator()), $predicate);
    }

    /**
     * Returns an iterable that applies function to each element of fromIterable.
     *
     * @param IteratorAggregate $fromIterable
     * @param callable $transformer
     * @return IteratorAggregate
     */
    public static function transform(IteratorAggregate $fromIterable, callable $transformer) : IteratorAggregate
    {
        return new CallableIterable(
            function () use ($fromIterable, $transformer) {
                return Iterators::transform(Iterators::from($fromIterable->getIterator()), $transformer);
            }
        );
    }

    /**
     * Creates an iterable with the first limitSize elements of the given iterable.
     * If the original iterable does not contain that many elements,
     * the returned iterable will have the same behavior as the original iterable.
     *
     * @param IteratorAggregate $iterable
     * @param $limitSize
     * @return IteratorAggregate
     * @throws \InvalidArgumentException if $limitSize is negative
     */
    public static function limit(IteratorAggregate $iterable, int $limitSize) : IteratorAggregate
    {
        return new CallableIterable(
            function () use ($iterable, $limitSize) {
                return Iterators::limit(Iterators::from($iterable->getIterator()), $limitSize);
            }
        );
    }

    /**
     * Returns the element at the specified position in an iterable.
     *
     * @param IteratorAggregate $iterable
     * @param $position
     * @return mixed
     * @throws \OutOfBoundsException if position does not exist
     */
    public static function get(IteratorAggregate $iterable, int $position)
    {
        return Iterators::get(Iterators::from($iterable->getIterator()), $position);
    }

    /**
     * Returns a view of iterable that skips its first numberToSkip elements.
     * If iterable contains fewer than numberToSkip elements,
     * the returned iterable skips all of its elements.
     *
     * @param IteratorAggregate $iterable
     * @param int $numberToSkip
     * @return IteratorAggregate
     */
    public static function skip(IteratorAggregate $iterable, int $numberToSkip) : IteratorAggregate
    {
        return new CallableIterable(
            function () use ($iterable, $numberToSkip) {
                $iterator = Iterators::from($iterable->getIterator());
                Iterators::advance($iterator, $numberToSkip);
                return $iterator;
            }
        );
    }

    /**
     * Returns the number of elements in iterable.
     *
     * @param IteratorAggregate $iterable
     * @return int
     */
    public static function size(IteratorAggregate $iterable) : int
    {
        if ($iterable instanceof Countable) {
            return $iterable->count();
        }
        return Iterators::size(Iterators::from($iterable->getIterator()));
    }

    /**
     * Returns true if iterable contains any object for which Objects::equal(element, object) is true.
     *
     * @param IteratorAggregate $iterable
     * @param $element
     * @return boolean
     */
    public static function contains(IteratorAggregate $iterable, $element) : bool
    {
        return Iterators::contains(Iterators::from($iterable->getIterator()), $element);
    }

    /**
     * Determines if the given iterable contains no elements.
     *
     * @param IteratorAggregate $iterable
     * @return boolean
     */
    public static function isEmpty(IteratorAggregate $iterable) : bool
    {
        return Iterators::isEmpty(Iterators::from($iterable->getIterator()));
    }

    /**
     * Checks whether the elements provided by the given iterables are equal correspondingly.
     * It uses {@link Objects::equal()} for equality check.
     *
     * @param IteratorAggregate $iterable1
     * @param IteratorAggregate $iterable2
     * @return bool
     */
    public static function elementsEqual(IteratorAggregate $iterable1, IteratorAggregate $iterable2) : bool
    {
        return Iterators::elementsEqual(
            Iterators::from($iterable1->getIterator()),
            Iterators::from($iterable2->getIterator())
        );
    }

    /**
     * Returns a string representation of iterable, with the format [e1, e2, ..., en].
     *
     * @param IteratorAggregate $iterable
     * @return string
     */
    public static function toString(IteratorAggregate $iterable) : string
    {
        return Iterators::toString(Iterators::from($iterable->getIterator()));
    }
}

/**
 * It is not intended to be used in your code.
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
final class CallableIterable implements IteratorAggregate
{
    private $callable;

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

    public function getIterator()
    {
        return Functions::call($this->callable);
    }
}


<?php
declare(strict_types=1);

namespace precore\util;

use precore\lang\IllegalStateException;
use precore\lang\BaseObject;
use precore\util\concurrent\TimeUnit;
use RuntimeException;

/**
 * An object that measures elapsed time in microseconds. It is useful to measure
 * elapsed time using this class instead of direct calls to {@link microtime()} for a few reasons:
 *
 *

 *

 *     An alternate time source can be substituted, for testing or performance
 *     reasons.
 *

 *

 *     As documented by {@link microtime()}, the value returned has no absolute
 *     meaning, and can only be interpreted as relative to another timestamp
 *     returned by {@link microtime()} at a different time. {@link Stopwatch} is a
 *     more effective abstraction because it exposes only these relative values,
 *     not the absolute ones.
 *

 *

 *
 *
Basic usage:
 *

 *   $stopwatch = Stopwatch::createStarted();
 *   doSomething();
 *   $stopwatch->stop(); // optional
 *   $millis = $stopwatch->elapsed(TimeUnit::$MILLISECONDS);
 *   $log->info("time: " . $stopwatch); // formatted string like "12.3 ms"
 *

 *
 *

 *   Stopwatch methods are not idempotent; it is an error to start or stop a
 *   stopwatch that is already in the desired state.
 *

 *
 *
When testing code that uses this class, use
 * {@link Stopwatch::createUnstartedWith($ticker)} or {@link Stopwatch::createStartedWith($ticker)} to
 * supply a fake or mock ticker.
 *
 * @see https://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/base/Stopwatch.java
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
final class Stopwatch extends BaseObject
{
    /**
     * @var Ticker
     */
    private $ticker;

    /**
     * @var boolean
     */
    private $isRunning = false;

    /**
     * @var float
     */
    private $elapsedMicros = 0;

    /**
     * @var float
     */
    private $startTick = 0;

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

    /**
     * @return Stopwatch
     */
    public static function createUnstarted() : Stopwatch
    {
        return new Stopwatch(Ticker::systemTicker());
    }

    /**
     * @return Stopwatch
     */
    public static function createStarted() : Stopwatch
    {
        return self::createUnstarted()->start();
    }

    /**
     * @param Ticker $ticker
     * @return Stopwatch
     */
    public static function createUnstartedWith(Ticker $ticker) : Stopwatch
    {
        return new Stopwatch($ticker);
    }

    /**
     * @param Ticker $ticker
     * @return Stopwatch
     */
    public static function createStartedWith(Ticker $ticker) : Stopwatch
    {
        return self::createUnstartedWith($ticker)->start();
    }

    /**
     * Starts the stopwatch.
     *
     * @return $this
     * @throws IllegalStateException if this Stopwatch is already running
     */
    public function start() : Stopwatch
    {
        Preconditions::checkState(!$this->isRunning, 'This stopwatch is already running.');
        $this->isRunning = true;
        $this->startTick = $this->ticker->read();
        return $this;
    }

    /**
     * Stops the stopwatch. Future reads will return the fixed duration that had
     * elapsed up to this point.
     *
     * @throw IllegalStateException if it is already stopped
     * @return $this
     * @throws IllegalStateException if this Stopwatch is already stopped
     */
    public function stop() : Stopwatch
    {
        $tick = $this->ticker->read();
        Preconditions::checkState($this->isRunning, 'This stopwatch is already stopped.');
        $this->isRunning = false;
        $this->elapsedMicros += ($tick - $this->startTick);
        return $this;
    }

    /**
     * Sets the elapsed time for this stopwatch to zero,
     * and places it in a stopped state.
     *
     * @return $this
     */
    public function reset() : Stopwatch
    {
        $this->elapsedMicros = 0;
        $this->isRunning = false;
        return $this;
    }

    /**
     * @return boolean
     */
    public function isRunning() : bool
    {
        return $this->isRunning;
    }

    /**
     * @param TimeUnit $desiredUnit
     * @return float
     */
    public function elapsed(TimeUnit $desiredUnit) : float
    {
        return $desiredUnit->convert($this->elapsedMicros(), TimeUnit::$MICROSECONDS);
    }

    /**
     * @return float
     */
    private function elapsedMicros() : float
    {
        return $this->isRunning
            ? $this->ticker->read() - $this->startTick
            : $this->elapsedMicros;
    }

    public function toString() : string
    {
        $micros = $this->elapsedMicros();

        $value = $micros;
        $resultUnit = TimeUnit::$MICROSECONDS;
        /* @var $currentUnit TimeUnit */
        foreach (array_reverse(TimeUnit::values()) as $currentUnit) {
            $currentValue = $currentUnit->convert($micros, TimeUnit::$MICROSECONDS);
            if (floor($currentValue) > 0) {
                $value = $currentValue;
                $resultUnit = $currentUnit;
                break;
            }
        }
        return sprintf("%.4g %s", $value, self::abbreviate($resultUnit));
    }

    private static function abbreviate(TimeUnit $unit) : string
    {
        switch ($unit) {
            case TimeUnit::$MICROSECONDS:
                return "µs";
            case TimeUnit::$MILLISECONDS:
                return "ms";
            case TimeUnit::$SECONDS:
                return "s";
            case TimeUnit::$MINUTES:
                return "min";
            case TimeUnit::$HOURS:
                return "h";
            case TimeUnit::$DAYS:
                return "d";
            default:
                throw new RuntimeException("Unhandled TimeUnit " . $unit);
        }
    }
}


<?php
declare(strict_types=1);

namespace precore\util;

use precore\lang\BaseObject;
use precore\lang\ObjectInterface;
use ReflectionClass;

/**
 * Helper functions that can operate on any ObjectInterface.
 *
 * @package precore\util
 *
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
final class Objects extends BaseObject
{
    private function __construct()
    {
    }

    /**
     * Determines whether two possibly-null objects are equal. Returns:
     *
     *  - True, if $objA and $objB are both null
     *  - True, if $objA and $objB are both non-null,
     *    $objA and $objB are both ObjectInterface instances,
     *    and $objA->equals($objB) is true
     *  - True, if $objA == $objB
     *  - false in all other situations.
     *
     * @param $objA
     * @param $objB
     * @return boolean
     */
    public static function equal($objA, $objB) : bool
    {
        if ($objA === $objB) {
            return true;
        }
        if ($objA instanceof ObjectInterface && $objB instanceof ObjectInterface) {
            return $objA->equals($objB);
        }
        return $objA == $objB;
    }

    /**
     * Creates an instance of ToStringHelper.
     * This is helpful for implementing ObjectInterface::toString().
     *
     * @param $identifier
     * @throws \InvalidArgumentException
     * @return ToStringHelper
     */
    public static function toStringHelper($identifier) : ToStringHelper
    {
        Preconditions::checkArgument(
            is_object($identifier) || is_string($identifier),
            'An object, a string, or a ReflectionClass must be used as identifier'
        );
        $name = null;
        if ($identifier instanceof ReflectionClass) {
            $name = $identifier->getName();
        } elseif (is_object($identifier)) {
            $name = get_class($identifier);
        } elseif (is_string($identifier)) {
            $name = $identifier;
        }
        return new ToStringHelper($name);
    }
}


<?php
declare(strict_types=1);

namespace precore\util;

use ArrayObject;
use precore\lang\ObjectClass;
use Traversable;

/**
 * Predicate is a function, which has one parameter, and returns a boolean value.
 * This class provides the most common predicates through static factory methods.
 *
 * @package precore\util
 * @author Janos Szurovecz <szjani@szjani.hu>
 */
final class Predicates
{
    private function __construct()
    {
    }

    /**
     * This predicate always returns true.
     *
     * @return callable
     */
    public static function alwaysTrue() : callable
    {
        return function () {
            return true;
        };
    }

    /**
     * This predicate always returns false.
     *
     * @return callable
     */
    public static function alwaysFalse() : callable
    {
        return function () {
            return false;
        };
    }

    /**
     * This predicate returns the opposite of the given predicate.
     *
     * @param callable $predicate
     * @return callable
     */
    public static function not(callable $predicate) : callable
    {
        return function ($element) use ($predicate) {
            return !self::call($predicate, $element);
        };
    }

    /**
     * This predicate returns true if and only if the parameter is null.
     *
     * @return callable
     */
    public static function isNull() : callable
    {
        return function ($element) {
            return $element === null;
        };
    }

    /**
     * This predicate returns true if and only if the parameter is not null.
     *
     * @return callable
     */
    public static function notNull() : callable
    {
        return function ($element) {
            return $element !== null;
        };
    }

    /**
     * Helper method to call a predicate with the given element as parameter.
     *
     * @param callable $predicate
     * @param $element
     * @return boolean
     */
    public static function call(callable $predicate, $element) : bool
    {
        return (boolean) Functions::call($predicate, $element);
    }

    /**
     * Returns true if and only if all given predicates return true.
     *
     * @param callable[] $predicates
     * @return callable
     */
    public static function ands(callable ...$predicates) : callable
    {
        return function ($element) use ($predicates) {
            foreach ($predicates as $predicate) {
                if (!self::call($predicate, $element)) {
                    return false;
                }
            }
            return true;
        };
    }

    /**
     * Returns true if at least one of the given predicates return true.
     *
     * @param callable[] $predicates
     * @return callable
     */
    public static function ors(callable ...$predicates) : callable
    {
        return function ($element) use ($predicates) {
            foreach ($predicates as $predicate) {
                if (self::call($predicate, $element)) {
                    return true;
                }
            }
            return false;
        };
    }

    /**
     * Returns true if the predicate parameter is equal to the given target.
     * It uses {@link Objects::equal()} to test equality.
     *
     * @param $target
     * @return callable
     */
    public static function equalTo($target) : callable
    {
        return function ($element) use ($target) {
            return Objects::equal($element, $target);
        };
    }

    /**
     * Returns true if the predicate parameter is an instance of the given class.
     *
     * @param $class
     * @return callable
     */
    public static function instance(string $class) : callable
    {
        return function ($element) use ($class) {
            return is_a($element, $class);
        };
    }

    /**
     * Returns true if the predicate parameter is contained by the given {@link Traversable}.
     *
     * @param Traversable $traversable
     * @return callable
     */
    public static function in(Traversable $traversable) : callable
    {
        return function ($element) use ($traversable) {
            return Iterators::contains(Iterators::from($traversable), $element);
        };
    }

    /**
     * Returns true if the predicate parameter is contained by the given array.
     *
     * @param array $array
     * @return callable
     */
    public static function inArray(array $array) : callable
    {
        return self::in(new ArrayObject($array));
    }

    /**
     * Returns true if the predicate parameter matches to the given regular expression.
     *
     * @param $pattern
     * @return callable
     */
    public static function matches(string $pattern) : callable
    {
        return function ($element) use ($pattern) {
            return preg_match($pattern, $element) === 1;
        };
    }

    /**
     * Returns true for $x if $predicate($function($x)) is true.
     *
     * @param callable $predicate
     * @param callable $function
     * @return callable
     */
    public static function compose(callable $predicate, callable $function) : callable
    {
        return function ($element) use ($predicate, $function) {
            return Predicates::call($predicate, Functions::call($function, $element));
        };
    }

    /**
     * @param string $class
     * @return callable
     */
    public static function assignableFrom(string $class) : callable
    {
        return function ($inputClass) use ($class) {
            return ObjectClass::forName($inputClass)->isAssignableFrom(ObjectClass::forName($class));
        };
    }
}


<?php

namespace ilateral\SilverStripe\Searchable\Control;

use SilverStripe\i18n\i18n;
use SilverStripe\ORM\ArrayList;
use SilverStripe\View\SSViewer;
use SilverStripe\View\ArrayData;
use SilverStripe\Control\Director;
use SilverStripe\ORM\PaginatedList;
use SilverStripe\Control\Controller;
use SilverStripe\Subsites\Model\Subsite;
use ilateral\SilverStripe\Searchable\Searchable;
use SilverStripe\CMS\Controllers\ContentController;

/**
 * Controller responsible for handling search results
 *
 * @package Searchable
 */
class SearchResults extends Controller
{

    /**
     * Designate the URL segment of this controller, used when
     * generating links to this controller.
     *
     * @var string
     * @config
     */
    private static $url_segment = "results";

    /**
     * @config
     */
    private static $allowed_actions = [
        "object"
    ];

    /**
     * Setup default templates for this controller
     *
     * @var array
     */
    protected $templates = [
        "index" => [SearchResults::class, "Page"],
        "object" => [SearchResults::class . "_object", SearchResults::class, "Page"]
    ];

    public function getQuery()
    {
        return $this->request->getVar('Search');
    }

    public function Link($action = null)
    {
        return Controller::join_links(
            $this->config()->url_segment,
            $action
        );
    }

    public function AbsoluteLink($action = null)
    {
        return Controller::join_links(
            Director::absoluteBaseURL(),
            $this->Link($action)
        );
    }

    /**
     * If content controller exists, return it's menu function
     * @param int $level Menu level to return.
     * @return ArrayList
     */
    public function getMenu($level = 1)
    {
        if (class_exists(ContentController::class)) {
            $controller = ContentController::singleton();
            return $controller->getMenu($level);
        }
    }

    public function Menu($level)
    {
        return $this->getMenu();
    }

    /**
     * Overwrite default init to support subsites (if installed)
     *
     * @return void
     */
    protected function init()
    {
        parent::init();

        # Check for subsites and add support
        if (class_exists(Subsite::class)) {
            $subsite = Subsite::currentSubsite();

            if ($subsite && $subsite->Theme) {
                SSViewer::add_themes([$subsite->Theme]);
            }

            if ($subsite && i18n::getData()->validate($subsite->Language)) {
                i18n::set_locale($subsite->Language);
            }
        }
    }

    public function index()
    {
        $keywords = $this->getQuery();
        $limit = Searchable::config()->dashboard_items;
        $classes_to_search = Searchable::config()->objects;
        $objects_list = ArrayList::create();

        // If only one class available, redirect to the object
        // action
        if (count($classes_to_search) == 1) {
            reset($classes_to_search);
            $classname = key($classes_to_search);
            return $this->redirect(Controller::join_links(
                $this->Link("object"),
                $classname,
                "?Search={$keywords}"
            ));
        }

        foreach ($classes_to_search as $classname => $cols) {
            $results = Searchable::Results(
                $classname,
                $cols,
                $keywords,
                $limit
            );

            if ($results->exists()) {
                $objects_list->add(ArrayData::create([
                    "Title" => _t($classname.".PLURALNAME", $classname),
                    "ClassName" => $classname,
                    "Results" => $results,
                    "Link" => Controller::join_links(
                        $this->Link("object"),
                        $classname,
                        "?Search={$keywords}"
                    )
                ]));
            }
        }

        $this->customise(array(
            "MetaTitle" => _t(
                "Searchable.TopSearchResults",
                "Top Search Results for '{query}'",
                'This is the title used for viewing the top results of a search for each object',
                ['query' => $this->getQuery()]
            ),
            "Objects" => $objects_list
        ));

        $this->extend("onBeforeIndex");

        return $this->render();
    }

    public function object()
    {
        $class_param = $this->request->param("ID");
        $classes_to_search = Searchable::config()->objects;
        $page_length = Searchable::config()->page_length;
        $cols = [];
        $classname = null;

        foreach ($classes_to_search as $search_class => $columns) {
            if ($class_param == $search_class) {
                $classname = $search_class;
                $cols = $columns;
            }
        }

        if (!count($cols)) {
            return $this->httpError(
                500,
                "No searchable classes configured"
            );
        }

        $keywords = $this->getQuery();

        $this->customise([
            "MetaTitle" => _t(
                'Searchable.SearchResultsFor',
                "Search Results for '{query}'",
                'This is the title used for viewing the results of a search',
                ['query' => $this->getQuery()]
            ),
            "Results" => PaginatedList::create(
                Searchable::Results($class_param, $cols, $keywords),
                $this->request
            )->setPageLength($page_length)
        ]);

        $this->extend("onBeforeObject");

        // Add the current object classname to the start of the
        // templates array before render
        $templates = $this->templates["object"];
        array_unshift(
            $templates,
            SearchResults::class . "_{$classname}"
        );
        $this->templates["object"] = $templates;

        return $this->render();
    }
}


<?php

/**
* The class to login to a web site with Google account
*
* LICENSE: MIT
*
* @category   Auth
* @package    Gelembjuk/Auth
* @copyright  Copyright (c) 2015 Roman Gelembjuk. (http://gelembjuk.com)
* @version    1.0
* @link       https://github.com/Gelembjuk/auth
*/
namespace Gelembjuk\Auth\SocialLogin;

class Google extends Base {
	/**
	 * To remember redirect url during auth session
	 *
	 * @var string
	 */
	protected $redirecturl;
	/**
	 * Google API object
	 *
	 * @var Google_Client
	 */
	protected $google;
	/**
	 * Google API token
	 *
	 * @var string
	 */
	protected $access_token;

	/**
     * Checks if an integration is configured. All options are provided.
     * This doesn't check if options are correct
     *
     * @return bool
     */
    public function isConfigured() {
        if (empty($this->options['application_name']) ||
            empty($this->options['client_id']) ||
            empty($this->options['client_secret'])) {
            return false;
        }
        return true;
    }

	/**
	 * Returns list of properties to serialise.
	 *
	 * @param array $skip Defines what properties to skip
	 */
	protected function getSerializeVars($skip = array()) {
		$skip[] = 'google';
		return parent::getSerializeVars($skip);
	}
	/**
	 * Returns Google API object inited with API settings
	 *
	 * @return Google_Client
	 */
	protected function getClient($redirecturl = '') {
		// keep only one instance during a session
		if (is_object($this->google)) {
			return $this->google;
		}
		if ($redirecturl == '') {
			$redirecturl = $this->redirecturl;
		} else {
			$this->redirecturl = $redirecturl;
		}

		$client = new \Google_Client();
		$client->setApplicationName($this->options['application_name']);
		$client->setClientId($this->options['client_id']);
		$client->setClientSecret($this->options['client_secret']);
		$client->setRedirectUri($redirecturl);
		$client->setDeveloperKey($this->options['api_key']);
		$client->setScopes(array('https://www.googleapis.com/auth/userinfo.profile','https://www.googleapis.com/auth/userinfo.email'));

		$this->google = $client;

		return $client;
	}
	/**
	 * Returns google login start auth process
	 *
	 * @param string $redirecturl URL where to redirect after login complete
	 */
	public function getLoginStartUrl($redirecturl) {
		$client = $this->getClient($redirecturl);
		$authUrl = $client->createAuthUrl();
		return $authUrl;
	}
	/**
	 * Get array of GET/POST arguments posted from google back to complete login URL
	 *
	 * @return array List of POST/GET arguments names
	 */
	public function getFinalExtraInputs() {
		return array('code','error');
	}
	/**
	 * Completes social login. Is caled after redirect from google auth page
	 *
	 * @param array $extrainputs List of POST/GET arguments names
	 */
	public function completeLogin($extrainputs = array()) {
		if ($extrainputs['code'] == '' && $extrainputs['error'] != '') {
			throw new \Exception($extrainputs['error']);
		}
		$client = $this->getClient();

		$client->authenticate($extrainputs['code']);
		$this->access_token = $client->getAccessToken();

		return $this->getUserProfile();
	}
	/**
	 * Returns short google profile after succes login
	 *
	 * @return array User Profile
	 */
	public function getUserProfile() {
		$client = $this->getClient();

		$client->setAccessToken($this->access_token);

		$plus = new \Google_Service_Plus($client);
		$oauth2 = new \Google_Service_Oauth2($client);

		if ($client->getAccessToken()) {
            $user = $oauth2->userinfo->get();

			if (isset($user->id)) {
                $name = $user->givenName;

                if (!empty($user->familyName)) {
                    $name = $user->familyName.' '.$user->givenName;
                }

				$profile = array(
					'userid'=>$user->id,
					'name' => $name,
					'imageurl' => $user->picture,
					'email' => $user->email
					);

				return $profile;
			}
  		}
  		throw new \Exception('Can not get google profile');
	}
}