/home/kueuepay/public_html/vendor/dragonmantank/cron-expression/src/Cron/CronExpression.php
<?php

declare(strict_types=1);

namespace Cron;

use DateTime;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use Exception;
use InvalidArgumentException;
use LogicException;
use RuntimeException;
use Webmozart\Assert\Assert;

/**
 * CRON expression parser that can determine whether or not a CRON expression is
 * due to run, the next run date and previous run date of a CRON expression.
 * The determinations made by this class are accurate if checked run once per
 * minute (seconds are dropped from date time comparisons).
 *
 * Schedule parts must map to:
 * minute [0-59], hour [0-23], day of month, month [1-12|JAN-DEC], day of week
 * [1-7|MON-SUN], and an optional year.
 *
 * @see http://en.wikipedia.org/wiki/Cron
 */
class CronExpression
{
    public const MINUTE = 0;
    public const HOUR = 1;
    public const DAY = 2;
    public const MONTH = 3;
    public const WEEKDAY = 4;

    /** @deprecated */
    public const YEAR = 5;

    public const MAPPINGS = [
        '@yearly' => '0 0 1 1 *',
        '@annually' => '0 0 1 1 *',
        '@monthly' => '0 0 1 * *',
        '@weekly' => '0 0 * * 0',
        '@daily' => '0 0 * * *',
        '@midnight' => '0 0 * * *',
        '@hourly' => '0 * * * *',
    ];

    /**
     * @var array CRON expression parts
     */
    protected $cronParts;

    /**
     * @var FieldFactoryInterface CRON field factory
     */
    protected $fieldFactory;

    /**
     * @var int Max iteration count when searching for next run date
     */
    protected $maxIterationCount = 1000;

    /**
     * @var array Order in which to test of cron parts
     */
    protected static $order = [
        self::YEAR,
        self::MONTH,
        self::DAY,
        self::WEEKDAY,
        self::HOUR,
        self::MINUTE,
    ];

    /**
     * @var array<string, string>
     */
    private static $registeredAliases = self::MAPPINGS;

    /**
     * Registered a user defined CRON Expression Alias.
     *
     * @throws LogicException If the expression or the alias name are invalid
     *                         or if the alias is already registered.
     */
    public static function registerAlias(string $alias, string $expression): void
    {
        try {
            new self($expression);
        } catch (InvalidArgumentException $exception) {
            throw new LogicException("The expression `$expression` is invalid", 0, $exception);
        }

        $shortcut = strtolower($alias);
        if (1 !== preg_match('/^@\w+$/', $shortcut)) {
            throw new LogicException("The alias `$alias` is invalid. It must start with an `@` character and contain alphanumeric (letters, numbers, regardless of case) plus underscore (_).");
        }

        if (isset(self::$registeredAliases[$shortcut])) {
            throw new LogicException("The alias `$alias` is already registered.");
        }

        self::$registeredAliases[$shortcut] = $expression;
    }

    /**
     * Unregistered a user defined CRON Expression Alias.
     *
     * @throws LogicException If the user tries to unregister a built-in alias
     */
    public static function unregisterAlias(string $alias): bool
    {
        $shortcut = strtolower($alias);
        if (isset(self::MAPPINGS[$shortcut])) {
            throw new LogicException("The alias `$alias` is a built-in alias; it can not be unregistered.");
        }

        if (!isset(self::$registeredAliases[$shortcut])) {
            return false;
        }

        unset(self::$registeredAliases[$shortcut]);

        return true;
    }

    /**
     * Tells whether a CRON Expression alias is registered.
     */
    public static function supportsAlias(string $alias): bool
    {
        return isset(self::$registeredAliases[strtolower($alias)]);
    }

    /**
     * Returns all registered aliases as an associated array where the aliases are the key
     * and their associated expressions are the values.
     *
     * @return array<string, string>
     */
    public static function getAliases(): array
    {
        return self::$registeredAliases;
    }

    /**
     * @deprecated since version 3.0.2, use __construct instead.
     */
    public static function factory(string $expression, FieldFactoryInterface $fieldFactory = null): CronExpression
    {
        /** @phpstan-ignore-next-line */
        return new static($expression, $fieldFactory);
    }

    /**
     * Validate a CronExpression.
     *
     * @param string $expression the CRON expression to validate
     *
     * @return bool True if a valid CRON expression was passed. False if not.
     */
    public static function isValidExpression(string $expression): bool
    {
        try {
            new CronExpression($expression);
        } catch (InvalidArgumentException $e) {
            return false;
        }

        return true;
    }

    /**
     * Parse a CRON expression.
     *
     * @param string $expression CRON expression (e.g. '8 * * * *')
     * @param null|FieldFactoryInterface $fieldFactory Factory to create cron fields
     * @throws InvalidArgumentException
     */
    public function __construct(string $expression, FieldFactoryInterface $fieldFactory = null)
    {
        $shortcut = strtolower($expression);
        $expression = self::$registeredAliases[$shortcut] ?? $expression;

        $this->fieldFactory = $fieldFactory ?: new FieldFactory();
        $this->setExpression($expression);
    }

    /**
     * Set or change the CRON expression.
     *
     * @param string $value CRON expression (e.g. 8 * * * *)
     *
     * @throws \InvalidArgumentException if not a valid CRON expression
     *
     * @return CronExpression
     */
    public function setExpression(string $value): CronExpression
    {
        $split = preg_split('/\s/', $value, -1, PREG_SPLIT_NO_EMPTY);
        Assert::isArray($split);

        $notEnoughParts = \count($split) < 5;

        $questionMarkInInvalidPart = array_key_exists(0, $split) && $split[0] === '?'
            || array_key_exists(1, $split) && $split[1] === '?'
            || array_key_exists(3, $split) && $split[3] === '?';

        $tooManyQuestionMarks = array_key_exists(2, $split) && $split[2] === '?'
            && array_key_exists(4, $split) && $split[4] === '?';

        if ($notEnoughParts || $questionMarkInInvalidPart || $tooManyQuestionMarks) {
            throw new InvalidArgumentException(
                $value . ' is not a valid CRON expression'
            );
        }

        $this->cronParts = $split;
        foreach ($this->cronParts as $position => $part) {
            $this->setPart($position, $part);
        }

        return $this;
    }

    /**
     * Set part of the CRON expression.
     *
     * @param int $position The position of the CRON expression to set
     * @param string $value The value to set
     *
     * @throws \InvalidArgumentException if the value is not valid for the part
     *
     * @return CronExpression
     */
    public function setPart(int $position, string $value): CronExpression
    {
        if (!$this->fieldFactory->getField($position)->validate($value)) {
            throw new InvalidArgumentException(
                'Invalid CRON field value ' . $value . ' at position ' . $position
            );
        }

        $this->cronParts[$position] = $value;

        return $this;
    }

    /**
     * Set max iteration count for searching next run dates.
     *
     * @param int $maxIterationCount Max iteration count when searching for next run date
     *
     * @return CronExpression
     */
    public function setMaxIterationCount(int $maxIterationCount): CronExpression
    {
        $this->maxIterationCount = $maxIterationCount;

        return $this;
    }

    /**
     * Get a next run date relative to the current date or a specific date
     *
     * @param string|\DateTimeInterface $currentTime      Relative calculation date
     * @param int                       $nth              Number of matches to skip before returning a
     *                                                    matching next run date.  0, the default, will return the
     *                                                    current date and time if the next run date falls on the
     *                                                    current date and time.  Setting this value to 1 will
     *                                                    skip the first match and go to the second match.
     *                                                    Setting this value to 2 will skip the first 2
     *                                                    matches and so on.
     * @param bool                      $allowCurrentDate Set to TRUE to return the current date if
     *                                                    it matches the cron expression.
     * @param null|string               $timeZone         TimeZone to use instead of the system default
     *
     * @throws \RuntimeException on too many iterations
     * @throws \Exception
     *
     * @return \DateTime
     */
    public function getNextRunDate($currentTime = 'now', int $nth = 0, bool $allowCurrentDate = false, $timeZone = null): DateTime
    {
        return $this->getRunDate($currentTime, $nth, false, $allowCurrentDate, $timeZone);
    }

    /**
     * Get a previous run date relative to the current date or a specific date.
     *
     * @param string|\DateTimeInterface $currentTime      Relative calculation date
     * @param int                       $nth              Number of matches to skip before returning
     * @param bool                      $allowCurrentDate Set to TRUE to return the
     *                                                    current date if it matches the cron expression
     * @param null|string               $timeZone         TimeZone to use instead of the system default
     *
     * @throws \RuntimeException on too many iterations
     * @throws \Exception
     *
     * @return \DateTime
     *
     * @see \Cron\CronExpression::getNextRunDate
     */
    public function getPreviousRunDate($currentTime = 'now', int $nth = 0, bool $allowCurrentDate = false, $timeZone = null): DateTime
    {
        return $this->getRunDate($currentTime, $nth, true, $allowCurrentDate, $timeZone);
    }

    /**
     * Get multiple run dates starting at the current date or a specific date.
     *
     * @param int $total Set the total number of dates to calculate
     * @param string|\DateTimeInterface|null $currentTime Relative calculation date
     * @param bool $invert Set to TRUE to retrieve previous dates
     * @param bool $allowCurrentDate Set to TRUE to return the
     *                               current date if it matches the cron expression
     * @param null|string $timeZone TimeZone to use instead of the system default
     *
     * @return \DateTime[] Returns an array of run dates
     */
    public function getMultipleRunDates(int $total, $currentTime = 'now', bool $invert = false, bool $allowCurrentDate = false, $timeZone = null): array
    {
        $timeZone = $this->determineTimeZone($currentTime, $timeZone);

        if ('now' === $currentTime) {
            $currentTime = new DateTime();
        } elseif ($currentTime instanceof DateTime) {
            $currentTime = clone $currentTime;
        } elseif ($currentTime instanceof DateTimeImmutable) {
            $currentTime = DateTime::createFromFormat('U', $currentTime->format('U'));
        } elseif (\is_string($currentTime)) {
            $currentTime = new DateTime($currentTime);
        }

        Assert::isInstanceOf($currentTime, DateTime::class);
        $currentTime->setTimezone(new DateTimeZone($timeZone));

        $matches = [];
        for ($i = 0; $i < $total; ++$i) {
            try {
                $result = $this->getRunDate($currentTime, 0, $invert, $allowCurrentDate, $timeZone);
            } catch (RuntimeException $e) {
                break;
            }

            $allowCurrentDate = false;
            $currentTime = clone $result;
            $matches[] = $result;
        }

        return $matches;
    }

    /**
     * Get all or part of the CRON expression.
     *
     * @param int|string|null $part specify the part to retrieve or NULL to get the full
     *                     cron schedule string
     *
     * @return null|string Returns the CRON expression, a part of the
     *                     CRON expression, or NULL if the part was specified but not found
     */
    public function getExpression($part = null): ?string
    {
        if (null === $part) {
            return implode(' ', $this->cronParts);
        }

        if (array_key_exists($part, $this->cronParts)) {
            return $this->cronParts[$part];
        }

        return null;
    }

    /**
     * Gets the parts of the cron expression as an array.
     *
     * @return string[]
     *   The array of parts that make up this expression.
     */
    public function getParts()
    {
        return $this->cronParts;
    }

    /**
     * Helper method to output the full expression.
     *
     * @return string Full CRON expression
     */
    public function __toString(): string
    {
        return (string) $this->getExpression();
    }

    /**
     * Determine if the cron is due to run based on the current date or a
     * specific date.  This method assumes that the current number of
     * seconds are irrelevant, and should be called once per minute.
     *
     * @param string|\DateTimeInterface $currentTime Relative calculation date
     * @param null|string               $timeZone    TimeZone to use instead of the system default
     *
     * @return bool Returns TRUE if the cron is due to run or FALSE if not
     */
    public function isDue($currentTime = 'now', $timeZone = null): bool
    {
        $timeZone = $this->determineTimeZone($currentTime, $timeZone);

        if ('now' === $currentTime) {
            $currentTime = new DateTime();
        } elseif ($currentTime instanceof DateTime) {
            $currentTime = clone $currentTime;
        } elseif ($currentTime instanceof DateTimeImmutable) {
            $currentTime = DateTime::createFromFormat('U', $currentTime->format('U'));
        } elseif (\is_string($currentTime)) {
            $currentTime = new DateTime($currentTime);
        }

        Assert::isInstanceOf($currentTime, DateTime::class);
        $currentTime->setTimezone(new DateTimeZone($timeZone));

        // drop the seconds to 0
        $currentTime->setTime((int) $currentTime->format('H'), (int) $currentTime->format('i'), 0);

        try {
            return $this->getNextRunDate($currentTime, 0, true)->getTimestamp() === $currentTime->getTimestamp();
        } catch (Exception $e) {
            return false;
        }
    }

    /**
     * Get the next or previous run date of the expression relative to a date.
     *
     * @param string|\DateTimeInterface|null $currentTime Relative calculation date
     * @param int $nth Number of matches to skip before returning
     * @param bool $invert Set to TRUE to go backwards in time
     * @param bool $allowCurrentDate Set to TRUE to return the
     *                               current date if it matches the cron expression
     * @param string|null $timeZone  TimeZone to use instead of the system default
     *
     * @throws \RuntimeException on too many iterations
     * @throws Exception
     *
     * @return \DateTime
     */
    protected function getRunDate($currentTime = null, int $nth = 0, bool $invert = false, bool $allowCurrentDate = false, $timeZone = null): DateTime
    {
        $timeZone = $this->determineTimeZone($currentTime, $timeZone);

        if ($currentTime instanceof DateTime) {
            $currentDate = clone $currentTime;
        } elseif ($currentTime instanceof DateTimeImmutable) {
            $currentDate = DateTime::createFromFormat('U', $currentTime->format('U'));
        } elseif (\is_string($currentTime)) {
            $currentDate = new DateTime($currentTime);
        } else {
            $currentDate = new DateTime('now');
        }

        Assert::isInstanceOf($currentDate, DateTime::class);
        $currentDate->setTimezone(new DateTimeZone($timeZone));
        // Workaround for setTime causing an offset change: https://bugs.php.net/bug.php?id=81074
        $currentDate = DateTime::createFromFormat("!Y-m-d H:iO", $currentDate->format("Y-m-d H:iP"), $currentDate->getTimezone());
        if ($currentDate === false) {
            throw new \RuntimeException('Unable to create date from format');
        }
        $currentDate->setTimezone(new DateTimeZone($timeZone));

        $nextRun = clone $currentDate;

        // We don't have to satisfy * or null fields
        $parts = [];
        $fields = [];
        foreach (self::$order as $position) {
            $part = $this->getExpression($position);
            if (null === $part || '*' === $part) {
                continue;
            }
            $parts[$position] = $part;
            $fields[$position] = $this->fieldFactory->getField($position);
        }

        if (isset($parts[self::DAY]) && isset($parts[self::WEEKDAY])) {
            $domExpression = sprintf('%s %s %s %s *', $this->getExpression(0), $this->getExpression(1), $this->getExpression(2), $this->getExpression(3));
            $dowExpression = sprintf('%s %s * %s %s', $this->getExpression(0), $this->getExpression(1), $this->getExpression(3), $this->getExpression(4));

            $domExpression = new self($domExpression);
            $dowExpression = new self($dowExpression);

            $domRunDates = $domExpression->getMultipleRunDates($nth + 1, $currentTime, $invert, $allowCurrentDate, $timeZone);
            $dowRunDates = $dowExpression->getMultipleRunDates($nth + 1, $currentTime, $invert, $allowCurrentDate, $timeZone);

            if ($parts[self::DAY] === '?' || $parts[self::DAY] === '*') {
                $domRunDates = [];
            }

            if ($parts[self::WEEKDAY] === '?' || $parts[self::WEEKDAY] === '*') {
                $dowRunDates = [];
            }

            $combined = array_merge($domRunDates, $dowRunDates);
            usort($combined, function ($a, $b) {
                return $a->format('Y-m-d H:i:s') <=> $b->format('Y-m-d H:i:s');
            });
            if ($invert) {
                $combined = array_reverse($combined);
            }

            return $combined[$nth];
        }

        // Set a hard limit to bail on an impossible date
        for ($i = 0; $i < $this->maxIterationCount; ++$i) {
            foreach ($parts as $position => $part) {
                $satisfied = false;
                // Get the field object used to validate this part
                $field = $fields[$position];
                // Check if this is singular or a list
                if (false === strpos($part, ',')) {
                    $satisfied = $field->isSatisfiedBy($nextRun, $part, $invert);
                } else {
                    foreach (array_map('trim', explode(',', $part)) as $listPart) {
                        if ($field->isSatisfiedBy($nextRun, $listPart, $invert)) {
                            $satisfied = true;

                            break;
                        }
                    }
                }

                // If the field is not satisfied, then start over
                if (!$satisfied) {
                    $field->increment($nextRun, $invert, $part);

                    continue 2;
                }
            }

            // Skip this match if needed
            if ((!$allowCurrentDate && $nextRun == $currentDate) || --$nth > -1) {
                $this->fieldFactory->getField(self::MINUTE)->increment($nextRun, $invert, $parts[self::MINUTE] ?? null);
                continue;
            }

            return $nextRun;
        }

        // @codeCoverageIgnoreStart
        throw new RuntimeException('Impossible CRON expression');
        // @codeCoverageIgnoreEnd
    }

    /**
     * Workout what timeZone should be used.
     *
     * @param string|\DateTimeInterface|null $currentTime Relative calculation date
     * @param string|null $timeZone TimeZone to use instead of the system default
     *
     * @return string
     */
    protected function determineTimeZone($currentTime, ?string $timeZone): string
    {
        if (null !== $timeZone) {
            return $timeZone;
        }

        if ($currentTime instanceof DateTimeInterface) {
            return $currentTime->getTimezone()->getName();
        }

        return date_default_timezone_get();
    }
}
Kueue Pay | Contactless Payment System
top

Quick Steps to NFC Pay

Getting started with NFC Pay is simple and quick. Register your account, add your cards, and you're ready to make payments in no time. Whether you're paying at a store, sending money to a friend, or managing your merchant transactions, NFC Pay makes it easy and secure.

1

Register Your Account

Download the NFC Pay app and sign up with your email or phone number. Complete the registration process by verifying your identity, and set up your secure PIN to protect your account.

2

Add Your Cards

Link your debit or credit cards to your NFC Pay wallet. Simply scan your card or enter the details manually, and you’re set to load funds, shop, and pay with ease.

3

Make Payment

To pay, simply tap your phone or scan the QR code at checkout. You can also transfer money to other users with a few taps. Enjoy fast, contactless payments with top-notch security.

Advanced Security Features Designed to Protect Your Information Effectively

NFC Pay prioritizes your security with advanced features that safeguard every transaction. From SMS or email verification to end-to-end encryption, we've implemented robust measures to ensure your data is always protected. Our security systems are designed to prevent unauthorized access and provide you with a safe and reliable payment experience.

img

SMS or Email Verification

Receive instant alerts for every transaction to keep track of your account activities.

img

KYC Solution

Verify your identity through our Know Your Customer process to prevent fraud and enhance security.

img

Two Factor Authentication

Dramatically supply transparent backward deliverables before caward comp internal or "organic" sources.

img

End-to-End Encryption

All your data and transactions are encrypted, ensuring that your sensitive information remains private.

img

Behavior Tracking

Monitor unusual activity patterns to detect and prevent suspicious behavior in real-time.

Top Reasons to Choose Us for Reliable and Expert Solutions

With NFC Pay, you get a trusted platform backed by proven expertise and a commitment to quality. We put our customers first, offering innovative solutions tailored to your needs, ensuring every transaction is secure, swift, and seamless.

1

Proven Expertise

Our team brings years of experience in the digital payments industry to provide reliable services.

2

Commitment to Quality

We prioritize excellence, ensuring that every aspect of our platform meets the highest standards.

3

Customer-Centric Approach

Your needs drive our solutions, and we are dedicated to delivering a superior user experience.

4

Innovative Solutions

We continuously evolve, integrating the latest technologies to enhance your payment experience.

Customer Feedback: Real Experiences from Satisfied Clients and Partners

Hear from our users who trust NFC Pay for their everyday transactions. Our commitment to security, ease of use, and exceptional service shines through in their experiences. See why our clients choose NFC Pay for their payment needs and how it has transformed the way they manage their finances.

"NFC Pay has made my transactions incredibly simple and secure. The intuitive interface and quick payment options are game-changers for my business"

"I love how NFC Pay prioritizes security without compromising on convenience. The two-factor authentication and instant alerts give me peace of mind every time I use it."

"Setting up my merchant account was a breeze, and now I can accept payments effortlessly. NFC Pay has truly streamlined my operations, saving me time and hassle."

Get the NFC Pay App for Seamless Transactions Anytime, Anywhere

Unlock the full potential of NFC Pay by downloading our app, designed to bring secure, swift, and smart transactions to your fingertips. Whether you're paying at a store, transferring money to friends, or managing your business payments, the NFC Pay app makes it effortless. Available on both iOS and Android, it's your all-in-one solution for convenient and reliable digital payments. Download now and experience the future of payments!

img