diff --git a/.github/workflows/pr_tests.yml b/.github/workflows/pr_tests.yml
index cb58324..89131dc 100644
--- a/.github/workflows/pr_tests.yml
+++ b/.github/workflows/pr_tests.yml
@@ -3,7 +3,7 @@ name: CI
on: [push, pull_request]
jobs:
- phpunit:
+ phpunit-7-1:
strategy:
fail-fast: false
matrix:
@@ -20,5 +20,25 @@ jobs:
with:
php-version: ${{ matrix.php_version }}
coverage: xdebug
- - name: Unit Tests
- run: vendor/bin/phpunit --stderr
+ - name: Unit Tests with PHPUnit 7.1
+ run: vendor/bin/phpunit --stderr --version 7.1
+
+ phpunit-9-6:
+ strategy:
+ fail-fast: false
+ matrix:
+ php_version: ["8.1"]
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - uses: php-actions/composer@v6
+ with:
+ php_version: "7.4"
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php_version }}
+ coverage: xdebug
+ - name: Unit Tests with PHPUnit 9.6
+ run: vendor/bin/phpunit --stderr --version 9.6
diff --git a/docs/gpl-3.0.txt b/docs/gpl-3.0.txt
index 94a9ed0..2a00065 100644
--- a/docs/gpl-3.0.txt
+++ b/docs/gpl-3.0.txt
@@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program. If not, see .
+ along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
@@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
-.
+.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
-.
+.
diff --git a/docs/license.md b/docs/license.md
index 9136add..f48f382 100644
--- a/docs/license.md
+++ b/docs/license.md
@@ -9,6 +9,6 @@ Some portions of this work are licensed under the GNU Lesser General Public Lice
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-You should have received a copy of the GNU General Public License, and the GNU Lesser General Public License along with this program. If not, see .
+You should have received a copy of the GNU General Public License, and the GNU Lesser General Public License along with this program. If not, see .
You may contact the copyright holder through XOOPS Project:
diff --git a/index.php b/index.php
index ea14d56..db4baed 100644
--- a/index.php
+++ b/index.php
@@ -1,2 +1,2 @@
* @copyright 2017 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class Assert extends BaseAssertion
diff --git a/src/Database/TableLoad.php b/src/Database/TableLoad.php
index 7c244c4..4ba474f 100644
--- a/src/Database/TableLoad.php
+++ b/src/Database/TableLoad.php
@@ -22,7 +22,7 @@
* @package Xmf
* @author Richard Griffith
* @copyright 2013-2018 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class TableLoad
@@ -130,7 +130,7 @@ public static function countRows($table, $criteria = null)
$prefixedTable = $db->prefix($table);
$sql = 'SELECT COUNT(*) as `count` FROM ' . $prefixedTable . ' ';
if (isset($criteria) && is_subclass_of($criteria, '\CriteriaElement')) {
- /* @var \CriteriaCompo $criteria */
+ /** @var \CriteriaCompo $criteria */
$sql .= $criteria->renderWhere();
}
$result = $db->query($sql);
@@ -157,7 +157,7 @@ public static function extractRows($table, $criteria = null, $skipColumns = arra
$prefixedTable = $db->prefix($table);
$sql = 'SELECT * FROM ' . $prefixedTable . ' ';
if (isset($criteria) && is_subclass_of($criteria, '\CriteriaElement')) {
- /* @var \CriteriaCompo $criteria */
+ /** @var \CriteriaCompo $criteria */
$sql .= $criteria->renderWhere();
}
$rows = array();
diff --git a/src/Database/Tables.php b/src/Database/Tables.php
index 2aabaea..1f71a41 100644
--- a/src/Database/Tables.php
+++ b/src/Database/Tables.php
@@ -624,7 +624,7 @@ public function delete($table, $criteria)
if (is_scalar($criteria)) {
$where = $criteria;
} elseif (is_object($criteria)) {
- /* @var \CriteriaCompo $criteria */
+ /** @var \CriteriaCompo $criteria */
$where = $criteria->renderWhere();
}
$this->queue[] = "DELETE FROM `{$tableDef['name']}` {$where}";
@@ -685,7 +685,7 @@ public function update($table, $columns, $criteria, $quoteValue = true)
if (is_scalar($criteria)) {
$where = $criteria;
} elseif (is_object($criteria)) {
- /* @var \CriteriaCompo $criteria */
+ /** @var \CriteriaCompo $criteria */
$where = $criteria->renderWhere();
}
$colSql = '';
diff --git a/src/Database/index.php b/src/Database/index.php
index ea14d56..db4baed 100644
--- a/src/Database/index.php
+++ b/src/Database/index.php
@@ -1,2 +1,2 @@
* @author Richard Griffith
* @copyright 2011-2021 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class Debug extends \Kint
diff --git a/src/Highlighter.php b/src/Highlighter.php
index 7b5fd35..8390751 100644
--- a/src/Highlighter.php
+++ b/src/Highlighter.php
@@ -18,7 +18,7 @@
* @package Xmf
* @author Richard Griffith
* @copyright 2011-2018 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class Highlighter
diff --git a/src/IPAddress.php b/src/IPAddress.php
index 5939f42..b5baf37 100644
--- a/src/IPAddress.php
+++ b/src/IPAddress.php
@@ -18,7 +18,7 @@
* @package Xmf
* @author trabis
* @copyright 2018-2021 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class IPAddress
diff --git a/src/Jwt/JsonWebToken.php b/src/Jwt/JsonWebToken.php
index 2947b55..db23532 100644
--- a/src/Jwt/JsonWebToken.php
+++ b/src/Jwt/JsonWebToken.php
@@ -22,7 +22,7 @@
* @package Xmf
* @author Richard Griffith
* @copyright 2018-2023 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class JsonWebToken
diff --git a/src/Jwt/KeyFactory.php b/src/Jwt/KeyFactory.php
index 1f4c9ef..de6387c 100644
--- a/src/Jwt/KeyFactory.php
+++ b/src/Jwt/KeyFactory.php
@@ -22,7 +22,7 @@
* @package Xmf
* @author Richard Griffith
* @copyright 2016-2018 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class KeyFactory
diff --git a/src/Jwt/TokenFactory.php b/src/Jwt/TokenFactory.php
index 1936f4a..238a97a 100644
--- a/src/Jwt/TokenFactory.php
+++ b/src/Jwt/TokenFactory.php
@@ -21,7 +21,7 @@
* @package Xmf
* @author Richard Griffith
* @copyright 2016-2018 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class TokenFactory
diff --git a/src/Jwt/TokenReader.php b/src/Jwt/TokenReader.php
index fc71b49..8bdb199 100644
--- a/src/Jwt/TokenReader.php
+++ b/src/Jwt/TokenReader.php
@@ -22,7 +22,7 @@
* @package Xmf
* @author Richard Griffith
* @copyright 2016-2018 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class TokenReader
diff --git a/src/Jwt/index.php b/src/Jwt/index.php
index ea14d56..db4baed 100644
--- a/src/Jwt/index.php
+++ b/src/Jwt/index.php
@@ -1,2 +1,2 @@
* @copyright 2018 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class ArrayStorage extends \ArrayObject implements StorageInterface
diff --git a/src/Key/Basic.php b/src/Key/Basic.php
index d7118db..d2296d5 100644
--- a/src/Key/Basic.php
+++ b/src/Key/Basic.php
@@ -22,7 +22,7 @@
* @package Xmf
* @author Richard Griffith
* @copyright 2018-2023 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class Basic extends KeyAbstract
diff --git a/src/Key/FileStorage.php b/src/Key/FileStorage.php
index 32dacbb..26ac6ca 100644
--- a/src/Key/FileStorage.php
+++ b/src/Key/FileStorage.php
@@ -20,7 +20,7 @@
* @package Xmf
* @author Richard Griffith
* @copyright 2016-2018 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class FileStorage implements StorageInterface
diff --git a/src/Key/KeyAbstract.php b/src/Key/KeyAbstract.php
index e7e12fb..90bff2d 100644
--- a/src/Key/KeyAbstract.php
+++ b/src/Key/KeyAbstract.php
@@ -20,7 +20,7 @@
* @package Xmf
* @author Richard Griffith
* @copyright 2018-2023 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
abstract class KeyAbstract
diff --git a/src/Key/StorageInterface.php b/src/Key/StorageInterface.php
index d7bf87a..e714170 100644
--- a/src/Key/StorageInterface.php
+++ b/src/Key/StorageInterface.php
@@ -20,7 +20,7 @@
* @package Xmf
* @author Richard Griffith
* @copyright 2018 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
interface StorageInterface
diff --git a/src/Key/index.php b/src/Key/index.php
index ea14d56..db4baed 100644
--- a/src/Key/index.php
+++ b/src/Key/index.php
@@ -1,2 +1,2 @@
* @copyright 2011-2018 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class Language
diff --git a/src/Metagen.php b/src/Metagen.php
index 4de5c88..e52f374 100644
--- a/src/Metagen.php
+++ b/src/Metagen.php
@@ -19,7 +19,7 @@
* @author Richard Griffith
* @author trabis
* @copyright 2011-2018 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class Metagen
@@ -59,7 +59,7 @@ public static function assignTitle($title)
*/
public static function assignKeywords($keywords)
{
- if (!empty($keywords) && is_array($keywords)) {
+ if (!empty($keywords) && \is_array($keywords)) {
$keyword_tag = implode(', ', $keywords);
static::assignThemeMeta('keywords', $keyword_tag);
}
diff --git a/src/Module/Helper.php b/src/Module/Helper.php
index 340c31c..e5e886d 100644
--- a/src/Module/Helper.php
+++ b/src/Module/Helper.php
@@ -24,7 +24,7 @@
* @author trabis
* @author Richard Griffith
* @copyright 2011-2023 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class Helper extends GenericHelper
diff --git a/src/Module/Helper/AbstractHelper.php b/src/Module/Helper/AbstractHelper.php
index 92ae51b..01d7c1b 100644
--- a/src/Module/Helper/AbstractHelper.php
+++ b/src/Module/Helper/AbstractHelper.php
@@ -20,7 +20,7 @@
* @author trabis
* @author Richard Griffith
* @copyright 2016-2018 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
abstract class AbstractHelper
@@ -67,7 +67,7 @@ public function __construct($dirname = null)
if (isset($xoops)) {
$moduleHandler = $xoops->getHandlerModule();
} else {
- /* @var \XoopsModuleHandler $moduleHandler */
+ /** @var \XoopsModuleHandler $moduleHandler */
$moduleHandler = xoops_getHandler('module');
}
$this->module = $moduleHandler->getByDirname($dirname);
diff --git a/src/Module/Helper/Cache.php b/src/Module/Helper/Cache.php
index a72ac04..f23f564 100644
--- a/src/Module/Helper/Cache.php
+++ b/src/Module/Helper/Cache.php
@@ -22,7 +22,7 @@
* @author trabis
* @author Richard Griffith
* @copyright 2011-2018 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class Cache extends AbstractHelper
diff --git a/src/Module/Helper/GenericHelper.php b/src/Module/Helper/GenericHelper.php
index c24dd5a..6cbba60 100644
--- a/src/Module/Helper/GenericHelper.php
+++ b/src/Module/Helper/GenericHelper.php
@@ -25,7 +25,7 @@
* @author trabis
* @author Richard Griffith
* @copyright 2016-2023 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
abstract class GenericHelper extends AbstractHelper
@@ -141,7 +141,7 @@ protected function initObject()
) {
$this->module = $xoopsModule;
} else {
- /* @var \XoopsModuleHandler $module_handler */
+ /** @var \XoopsModuleHandler $module_handler */
$module_handler = xoops_getHandler('module');
$this->module = $module_handler->getByDirname($this->dirname);
}
@@ -163,7 +163,7 @@ protected function initConfig()
global $xoopsModuleConfig;
$this->configs = $xoopsModuleConfig;
} else {
- /* @var \XoopsConfigHandler $config_handler */
+ /** @var \XoopsConfigHandler $config_handler */
$config_handler = xoops_getHandler('config');
$this->configs = $config_handler->getConfigsByCat(0, $this->getModule()->getVar('mid'));
}
diff --git a/src/Module/Helper/Permission.php b/src/Module/Helper/Permission.php
index 822b39c..32bd3f9 100644
--- a/src/Module/Helper/Permission.php
+++ b/src/Module/Helper/Permission.php
@@ -21,7 +21,7 @@
* @author trabis
* @author Richard Griffith
* @copyright 2011-2023 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class Permission extends AbstractHelper
@@ -50,7 +50,7 @@ public function init()
{
$this->mid = $this->module->getVar('mid');
$this->dirname = $this->module->getVar('dirname');
- /* @var $this->permissionHandler XoopsGroupPermHandler */
+ /** @var $this->permissionHandler XoopsGroupPermHandler */
$this->permissionHandler = xoops_getHandler('groupperm');
}
diff --git a/src/Module/Helper/Session.php b/src/Module/Helper/Session.php
index c3d992a..879cdd2 100644
--- a/src/Module/Helper/Session.php
+++ b/src/Module/Helper/Session.php
@@ -22,7 +22,7 @@
* @author trabis
* @author Richard Griffith
* @copyright 2011-2018 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
*/
class Session extends AbstractHelper
diff --git a/src/Module/Helper/index.php b/src/Module/Helper/index.php
index ea14d56..db4baed 100644
--- a/src/Module/Helper/index.php
+++ b/src/Module/Helper/index.php
@@ -1,2 +1,2 @@
$value) {
- if (substr($name, 0, 5) === 'HTTP_') {
+ if ('HTTP_' === substr($name, 0, 5)) {
$translatedName = (string)str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($name, 5))));
$headers[$translatedName] = $value;
}
@@ -378,7 +377,7 @@ public static function getHeader($headerName, $default = '')
public static function hasVar($name, $hash = 'default')
{
$hash = strtoupper($hash);
- if ($hash === 'METHOD') {
+ if ('METHOD' === $hash) {
$hash = strtoupper($_SERVER['REQUEST_METHOD']);
}
@@ -393,9 +392,9 @@ public static function hasVar($name, $hash = 'default')
/**
* Set a variable in one of the request variables
*
- * @param string $name Name
- * @param string $value Value
- * @param string $hash Hash
+ * @param string $name Name
+ * @param string $value Value
+ * @param string $hash Hash
* @param bool $overwrite Boolean
*
* @return string Previous value
@@ -403,7 +402,7 @@ public static function hasVar($name, $hash = 'default')
public static function setVar($name, $value = null, $hash = 'method', $overwrite = true)
{
$hash = strtoupper($hash);
- if ($hash === 'METHOD') {
+ if ('METHOD' === $hash) {
$hash = strtoupper($_SERVER['REQUEST_METHOD']);
}
@@ -477,7 +476,7 @@ public static function get($hash = 'default', $mask = 0)
{
$hash = strtoupper($hash);
- if ($hash === 'METHOD') {
+ if ('METHOD' === $hash) {
$hash = strtoupper($_SERVER['REQUEST_METHOD']);
}
@@ -506,7 +505,7 @@ public static function get($hash = 'default', $mask = 0)
}
// Handle magic quotes compatibility
- if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() && ($hash !== 'FILES')) {
+ if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() && ('FILES' !== $hash)) {
$input = static::stripSlashesRecursive($input);
}
@@ -518,8 +517,8 @@ public static function get($hash = 'default', $mask = 0)
/**
* Sets a request variable
*
- * @param array $array An associative array of key-value pairs
- * @param string $hash The request variable to set (POST, GET, FILES, METHOD)
+ * @param array $array An associative array of key-value pairs
+ * @param string $hash The request variable to set (POST, GET, FILES, METHOD)
* @param bool $overwrite If true and an existing key is found, the value is overwritten,
* otherwise it is ignored
*
@@ -554,7 +553,7 @@ protected static function cleanVar($var, $mask = 0, $type = null)
static $safeHtmlFilter = null;
// convert $var in array if $type is ARRAY
- if (strtolower((string)$type) === 'array' && !is_array($var)) {
+ if ('array' === strtolower((string)$type) && !is_array($var)) {
$var = array($var);
}
diff --git a/src/StopWords.php b/src/StopWords.php
index 48723fe..211a869 100644
--- a/src/StopWords.php
+++ b/src/StopWords.php
@@ -19,7 +19,7 @@
* @author Richard Griffith
* @author trabis
* @copyright 2011-2018 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
* @see https://en.wikipedia.org/wiki/Stop_words
*/
diff --git a/src/Ulid.php b/src/Ulid.php
new file mode 100644
index 0000000..887119f
--- /dev/null
+++ b/src/Ulid.php
@@ -0,0 +1,193 @@
+
+ * @copyright 2023 XOOPS Project (https://xoops.org)
+ * @license GNU GPL 2 or later (https://www.gnu.org/licenses/gpl-2.0.html)
+ */
+class Ulid
+{
+ const ENCODING_CHARS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
+ const ENCODING_LENGTH = 32;
+
+ /**
+ * Generate a new ULID.
+ *
+ * @return string The generated ULID.
+ */
+ public static function generate(bool $upperCase = true): string
+ {
+ $time = self::microtimeToUlidTime(\microtime(true));
+ $timeChars = self::encodeTime($time);
+ $randChars = self::encodeRandomness();
+ $ulid = $timeChars . $randChars;
+
+ $ulid = $upperCase ? \strtoupper($ulid) : \strtolower($ulid);
+
+ return $ulid;
+ }
+
+ /**
+ * @param int $time
+ *
+ * @return string
+ */
+ public static function encodeTime(int $time): string
+ {
+ $encodingCharsArray = str_split(self::ENCODING_CHARS);
+ $timeChars = '';
+ for ($i = 0; $i < 10; $i++) {
+ $mod = \floor($time % self::ENCODING_LENGTH);
+ $timeChars = $encodingCharsArray[$mod] . $timeChars;
+ $time = (int)(($time - $mod) / self::ENCODING_LENGTH);
+ }
+ return $timeChars;
+ }
+
+ public static function encodeRandomness(): string
+ {
+ $encodingCharsArray = str_split(self::ENCODING_CHARS);
+ $randomBytes = \random_bytes(10); // 80 bits
+ // Check if the random bytes were generated successfully.
+ if (false === $randomBytes) {
+ throw new \RuntimeException('Failed to generate random bytes');
+ }
+
+ $randChars = '';
+ for ($i = 0; $i < 16; $i++) {
+ $randValue = \ord($randomBytes[$i % 10]);
+ if (0 === $i % 2) {
+ $randValue >>= 3; // take the upper 5 bits
+ } else {
+ $randValue &= 31; // take the lower 5 bits
+ }
+ $randChars .= $encodingCharsArray[$randValue];
+ }
+ return $randChars;
+ }
+
+ /**
+ * @param string $ulid
+ *
+ * @return array
+ */
+ public static function decode(string $ulid): array
+ {
+ if (!self::isValid($ulid)) {
+ throw new \InvalidArgumentException('Invalid ULID string');
+ }
+
+ $time = self::decodeTime($ulid);
+ $rand = self::decodeRandomness($ulid);
+
+ return [
+ 'time' => $time,
+ 'rand' => $rand,
+ ];
+ }
+
+ /**
+ * @param string $ulid
+ *
+ * @return int
+ */
+ public static function decodeTime(string $ulid): int
+ {
+// $encodingCharsArray = str_split(self::ENCODING_CHARS);
+
+ // Check if the ULID string is valid.
+ if (!self::isValid($ulid)) {
+ throw new \InvalidArgumentException('Invalid ULID string');
+ }
+
+ $time = 0;
+ for ($i = 0; $i < 10; $i++) {
+ $char = $ulid[$i];
+ $value = \strpos(self::ENCODING_CHARS, $char);
+ $exponent = 9 - $i;
+ $time += $value * \bcpow((string)self::ENCODING_LENGTH, (string)$exponent);
+ }
+
+ return $time;
+ }
+
+ /**
+ * @param string $ulid
+ *
+ * @return int
+ */
+ public static function decodeRandomness(string $ulid): int
+ {
+ if (26 !== strlen($ulid)) {
+ throw new \InvalidArgumentException('Invalid ULID length'); // Changed line
+ }
+
+ $rand = 0;
+ for ($i = 10; $i < 26; $i++) {
+ $char = $ulid[$i];
+ $value = \strpos(self::ENCODING_CHARS, $char);
+
+ // Check if the random value is within the valid range.
+ if ($value < 0 || $value >= self::ENCODING_LENGTH) {
+ throw new \InvalidArgumentException('Invalid ULID random value');
+ }
+ $exponent = 15 - $i;
+ $rand += $value * \bcpow((string)self::ENCODING_LENGTH, (string)$exponent);
+ }
+
+ return $rand;
+ }
+
+ /**
+ * @param string $ulid
+ *
+ * @return bool
+ */
+ public static function isValid(string $ulid): bool
+ {
+ // Check the length of the ULID string before throwing an exception.
+ if (26 !== strlen($ulid)) {
+ return false;
+ }
+
+ // Throw an exception if the ULID is invalid.
+ try {
+ self::decodeRandomness($ulid);
+ } catch (\InvalidArgumentException $e) {
+ return false;
+}
+
+ return true;
+ }
+
+ /**
+ * @param float $microtime
+ *
+ * @return int
+ */
+ public static function microtimeToUlidTime(float $microtime): int
+ {
+ $timestamp = $microtime * 1000000;
+ $unixEpoch = 946684800000000; // Microseconds since the Unix epoch.
+
+ return (int)($timestamp - $unixEpoch);
+ }
+}
+
+
+
diff --git a/src/Uuid.php b/src/Uuid.php
index fd8fec6..c90f444 100644
--- a/src/Uuid.php
+++ b/src/Uuid.php
@@ -18,7 +18,7 @@
* @package Xmf
* @author Richard Griffith
* @copyright 2017-2021 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (https://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
*/
class Uuid
{
@@ -28,7 +28,7 @@ class Uuid
/**
* generate - generate a version 4 (random) UUID
*
- * Based on comment by pavel.volyntsev(at)gmail at http://php.net/manual/en/function.com-create-guid.php
+ * Based on comment by pavel.volyntsev(at)gmail at https://php.net/manual/en/function.com-create-guid.php
*
* @return string UUID
*
diff --git a/src/Yaml.php b/src/Yaml.php
index f9c214c..f253d5a 100644
--- a/src/Yaml.php
+++ b/src/Yaml.php
@@ -30,7 +30,7 @@
* @package Xmf
* @author Richard Griffith
* @copyright 2013-2020 XOOPS Project (https://xoops.org)
- * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
+ * @license GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
* @link https://xoops.org
* @see http://www.yaml.org/
*/
diff --git a/src/index.php b/src/index.php
index ea14d56..db4baed 100644
--- a/src/index.php
+++ b/src/index.php
@@ -1,2 +1,2 @@
assertTrue(Ulid::isValid($ulid));
+
+ // Assert that the ULID string is always in uppercase.
+ $this->assertEquals($ulid, \strtoupper($ulid));
+
+ // Assert that the ULID string is unique.
+ $this->assertNotEquals($ulid, Ulid::generate());
+
+ $ulid1 = Ulid::generate();
+ $this->assertTrue(Ulid::isValid($ulid1));
+ \usleep(2000); // Wait for 2 milliseconds to ensure a different timestamp
+ $ulid2 = Ulid::generate();
+
+ $this->assertNotEquals($ulid1, $ulid2, 'ULIDs should be unique');
+ $this->assertTrue(\strcasecmp($ulid1, $ulid2) < 0, 'ULIDs should collate correctly');
+ }
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testGenerateUlidUpperCase()
+ {
+ $ulid = Ulid::generate(true);
+ $this->assertTrue(Ulid::isValid($ulid));
+ $this->assertEquals(\strtoupper($ulid), $ulid);
+ }
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testGenerateUlidLowerCase()
+ {
+ $ulid = Ulid::generate(false);
+ $this->assertTrue(Ulid::isValid($ulid));
+ $this->assertEquals(\strtolower($ulid), $ulid);
+ }
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testDecode()
+ {
+ $ulid = Ulid::generate();
+
+ // Decode the ULID string.
+ $components = Ulid::decode($ulid);
+
+ // Assert that the decoded time and randomness components are valid.
+ $this->assertGreaterThan(0, $components['time']);
+ $this->assertGreaterThan(0, $components['rand']);
+
+ // Assert that the decoded time and randomness components are within the valid range.
+ $this->assertLessThanOrEqual(PHP_INT_MAX, $components['time']);
+ $this->assertLessThanOrEqual(PHP_INT_MAX, $components['rand']);
+ }
+
+ /**
+ * Tests that the `generate()` method generates a lowercase ULID when configured to do so.
+ *
+ * @covers Xmf\Ulid::generate
+ * @throws \Exception
+ */
+ public function testGeneratesLowercaseIdentifierWhenConfigured()
+ {
+ $ulid = Ulid::generate(false); //generate lower case
+
+ if (\method_exists($this, 'assertMatchesRegularExpression')) {
+ $this->assertMatchesRegularExpression('/[0-9][a-z]/', $ulid);
+ } else {
+ $this->assertRegExp('/[0-9][a-z]/', $ulid);
+ }
+ }
+
+ /**
+ * Tests that the `generate()` method generates a 26-character ULID.
+ *
+ * @covers Xmf\Ulid::generate
+ * @throws \Exception
+ */
+ public function testGeneratesTwentySixChars()
+ {
+ $ulid = Ulid::generate();
+
+ $this->assertSame(26, \strlen($ulid));
+ }
+
+ /**
+ * Tests that the `generate()` method generates ULIDs with different random characters when generated multiple times.
+ *
+ * @covers Xmf\Ulid::generate
+ * @throws \Exception
+ * @throws \Exception
+ */
+ public function testRandomnessWhenGeneratedMultipleTimes()
+ {
+ $a = Ulid::generate();
+ \usleep(100); // Wait for 100 microseconds to ensure a different timestamp
+ $b = Ulid::generate();
+ $this->assertLessThan($b, $a);
+
+ // Using strcmp for lexicographical comparison
+ $this->assertTrue(\strcmp($a, $b) < 0);
+
+ // The time parts are different.
+ $this->assertNotEquals(\substr($a, 0, 10), \substr($b, 0, 10));
+
+ //the second ULID time part is bigger than the first ULID
+ $this->assertGreaterThan(\substr($a, 0, 10), \substr($b, 0, 10));
+
+ // The first 5-6 characters should be the same
+ $this->assertEquals(\substr($a, 0, 5), \substr($b, 0, 5));
+
+ //the random characters part should be different
+ $this->assertNotEquals(\substr($a, 10), \substr($b, 10));
+ }
+
+ /**
+ * Tests that the `generate()` method generates lexicographically sortable ULIDs.
+ *
+ * @covers Xmf\Ulid::generate
+ * @throws \Exception
+ * @throws \Exception
+ */
+ public function testGeneratesLexographicallySortableUlids()
+ {
+ $a = Ulid::generate();
+
+ \usleep(1000); // Wait for 1 millisecond to ensure a different timestamp
+
+ $b = Ulid::generate();
+
+ $ulids = [$b, $a];
+ \usort($ulids, 'strcmp');
+
+ $this->assertSame([$a, $b], $ulids);
+ }
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testIsValid()
+ {
+ $ulid = Ulid::generate();
+
+ // Assert that the isValid() method returns true for valid ULID strings.
+ $this->assertTrue(Ulid::isValid($ulid));
+
+ // Assert that the isValid() method returns false for invalid ULID strings.
+ $invalidUlid = 'invalid-ulid';
+ $this->assertFalse(Ulid::isValid($invalidUlid));
+ }
+
+ /**
+ * @return void
+ */
+ public function testEncodeTime()
+ {
+ $time = 572826470852228;
+ $timeChars = Ulid::encodeTime($time); // Assumes encodeTime is public
+
+ $this->assertEquals('G8ZE7509M4', $timeChars);
+ }
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testDecodeTime()
+ {
+ $timeChars = 'G8ZE7509M4SNQGYN4H6GSNQGYN';
+ $time = Ulid::decodeTime($timeChars);
+ $this->assertEquals(572826470852228, $time);
+ }
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testEncodeRandomness()
+ {
+ $randChars = Ulid::encodeRandomness();
+ //Checking the length of randomness characters
+ $this->assertEquals(16, \strlen($randChars));
+ }
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testDecodeRandomness()
+ {
+ $randChars = Ulid::generate();
+
+ $rand = Ulid::decodeRandomness($randChars);
+ // Assert that the randomness value is within the valid range.
+ $this->assertGreaterThanOrEqual(0, $rand);
+ $this->assertLessThanOrEqual(PHP_INT_MAX, $rand);
+
+ // Assert that the randomness value is different from a known value.
+ $this->assertNotEquals(1234567890, $rand);
+ }
+
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testGenerateUnique()
+ {
+ $ulid1 = Ulid::generate();
+ $ulid2 = Ulid::generate();
+
+ $this->assertNotEquals($ulid1, $ulid2);
+ }
+
+ /**
+ * @return void
+ */
+ public function testDecodeException()
+ {
+
+ $invalidUlid = 'invalid-ulid';
+
+ $this->expectException(\InvalidArgumentException::class);
+ Ulid::decode($invalidUlid);
+ }
+
+ /**
+ * @return void
+ */
+ public function testIsValidException()
+ {
+ $invalidUlid = 'invalid-ulid';
+
+ $isValid = Ulid::isValid($invalidUlid);
+
+ $this->assertFalse($isValid);
+ }
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testDecodeUlid()
+ {
+ $ulid = Ulid::generate();
+ $components = Ulid::decode($ulid);
+
+ $this->assertTrue($components['rand'] >= 0 && $components['rand'] < (32 ** 16));
+ }
+
+ /**
+ * @return void
+ */
+ public function testDecodeInvalidUlid()
+ {
+ $invalidUlid = 'invalid-ulid';
+
+ // Assert that the decode() method throws an exception of the correct type for invalid ULID strings.
+ $this->expectException(\InvalidArgumentException::class);
+ Ulid::decode($invalidUlid);
+ }
+
+ // Validate ULID
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testValidateUlid()
+ {
+ $ulid = Ulid::generate();
+
+ $this->assertTrue(Ulid::isValid($ulid));
+ }
+
+ /**
+ * @return void
+ */
+ public function testValidateInvalidUlid()
+ {
+ //INVALID
+ $this->assertFalse(Ulid::isValid('invalid-ulid-string'));
+ }
+
+ // Test ULID Generation:
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testUlidGeneration()
+ {
+ $ulid = Ulid::generate();
+ $this->assertNotEmpty($ulid);
+ $this->assertSame(26, \strlen($ulid));
+ }
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testUlidUniqueness()
+ {
+ $ulid1 = Ulid::generate();
+ \usleep(1000); // Wait for 1 millisecond to ensure a different timestamp
+ $ulid2 = Ulid::generate();
+ $this->assertNotEquals($ulid1, $ulid2);
+ }
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testCaseSensitivity()
+ {
+ $ulidUpperCase = Ulid::generate(true);
+ $ulidLowerCase = Ulid::generate(false);
+
+ // Assert that the two ULID strings are different.
+ $this->assertNotEquals($ulidUpperCase, $ulidLowerCase);
+
+ echo "ulidUpperCase: $ulidUpperCase\n";
+ echo "ulidLowerCase: $ulidLowerCase\n";
+ }
+
+ // Test ULID Decoding:
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testUlidDecoding()
+ {
+ $ulid = Ulid::generate();
+ $components = Ulid::decode($ulid);
+ $this->assertArrayHasKey('time', $components);
+ $this->assertArrayHasKey('rand', $components);
+ }
+
+ /**
+ * @return void
+ */
+ public function testInvalidUlidDecoding()
+ {
+
+ $this->expectException(\InvalidArgumentException::class);
+ Ulid::decode('invalidulid');
+ }
+
+ // Test ULID Encoding:
+ // (assuming you have a method to encode time and randomness separately)
+ /**
+ * @return void
+ */
+ public function testTimeEncoding()
+ {
+ $time = Ulid::microtimeToUlidTime(\microtime(true));
+ $encodedTime = Ulid::encodeTime($time); // Assumes encodeTime is public
+ $this->assertNotEmpty($encodedTime);
+ }
+
+ // Test ULID Validity Checking:
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testValidUlid()
+ {
+ $ulid = Ulid::generate();
+ $this->assertTrue(Ulid::isValid($ulid));
+ }
+
+ /**
+ * @return void
+ */
+ public function testInvalidUlid()
+ {
+ $this->assertFalse(Ulid::isValid('invalidulid'));
+ }
+
+ // Test Lexicographic Order:
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testLexicographicOrder()
+ {
+ $ulid1 = Ulid::generate();
+ \usleep(1000);
+ $ulid2 = Ulid::generate();
+ $this->assertTrue(\strcmp($ulid1, $ulid2) < 0);
+ }
+
+ // Test Microtime Conversion:
+
+ /**
+ * @return void
+ */
+ public function testMicrotimeConversion()
+ {
+ $microtime = \microtime(true);
+ $ulidTime = Ulid::microtimeToUlidTime($microtime);
+
+ $this->assertIsInt($ulidTime);
+ // Check if the time is within a reasonable range (e.g., since the year 2000)
+ $this->assertGreaterThanOrEqual($ulidTime, 946684800000000);
+ }
+
+ // Test the decoding of time from a given ULID
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testDecodeTimeInt()
+ {
+ $ulid = Ulid::generate();
+ $decodedTime = Ulid::decodeTime($ulid); // Assumes decodeTime is public
+ $this->assertIsInt($decodedTime);
+ }
+
+ // Test the decoding of randomness from a given ULID
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testDecodeRandomnessInt()
+ {
+ $ulid = Ulid::generate();
+ $decodedRandomness = Ulid::decodeRandomness($ulid); // Assumes decodeRandomness is public
+
+ $this->assertIsInt($decodedRandomness);
+ }
+
+ // Test the encoding of randomness
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testEncodeRandomnessNotEmpty()
+ {
+ $encodedRandomness = Ulid::encodeRandomness(); // Assumes encodeRandomness is public
+ $this->assertNotEmpty($encodedRandomness);
+ }
+
+ // Test for valid random value range
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testRandomValueRange()
+ {
+ $ulid = Ulid::generate();
+ $components = Ulid::decode($ulid);
+ $this->assertGreaterThanOrEqual(0, $components['rand']);
+ // Assuming a maximum value for the random component:
+ $this->assertLessThanOrEqual(PHP_INT_MAX, $components['rand']);
+ }
+
+ // Test encoding and decoding consistency
+
+ /**
+ * @return void
+ * @throws \Exception
+ */
+ public function testEncodingDecodingConsistency()
+ {
+ $ulid = Ulid::generate();
+ $components = Ulid::decode($ulid);
+ $encodedTime = Ulid::encodeTime($components['time']); // Assumes encodeTime is public
+ $this->assertEquals(\substr($ulid, 0, 10), $encodedTime);
+ }
+
+ // Test if the microtimeToUlidTime function is working as expected
+
+ /**
+ * @return void
+ */
+ public function testMicrotimeToUlidTimeFunction()
+ {
+ $microtime = \microtime(true);
+ $ulidTime = Ulid::microtimeToUlidTime($microtime);
+
+ $this->assertIsInt($ulidTime);
+
+ // Check if the time is within a reasonable range (e.g., since the year 2000)
+ $this->assertGreaterThanOrEqual($ulidTime, 946684800000000);
+ }
+
+ /**
+ * Test for valid ULID string format
+ * @throws \Exception
+ */
+ public function testValidUlidString()
+ {
+ $ulid = Ulid::generate();
+
+ $this->assertTrue(Ulid::isValid($ulid));
+ }
+
+ /**
+ * Test for invalid ULID string format
+ */
+ public function testInvalidUlidString()
+ {
+ $invalidUlid = 'INVALID_ULID_STRING';
+
+ $this->assertFalse(Ulid::isValid($invalidUlid));
+ }
+
+ /**
+ * Test for case insensitivity in ULID validation
+ * @throws \Exception
+ */
+ public function testCaseInsensitivity()
+ {
+ $ulid = Ulid::generate(false); // generate lowercase ULID
+ $this->assertTrue(Ulid::isValid(\strtoupper($ulid)));
+ }
+
+ /**
+ * Test exception handling for invalid ULID string in decode method
+ */
+ public function testDecodeExceptionHandling()
+ {
+
+ $this->expectException(\InvalidArgumentException::class);
+ $invalidUlid = 'INVALID_ULID_STRING';
+ Ulid::decode($invalidUlid);
+ }
+
+ /**
+ * Test exception handling for invalid ULID string in decodeTime method
+ */
+ public function testDecodeTimeExceptionHandling()
+ {
+ $invalidUlid = 'INVALID_ULID_STRING';
+
+ $this->expectException(\InvalidArgumentException::class);
+ Ulid::decodeTime($invalidUlid); // Assumes decodeTime is public
+ }
+
+ /**
+ * Test exception handling for invalid ULID string in decodeRandomness method
+ */
+ public function testDecodeRandomnessExceptionHandling()
+ {
+ $invalidUlid = 'INVALID_ULID_STRING';
+
+ $this->expectException(\InvalidArgumentException::class);
+ Ulid::decodeRandomness($invalidUlid); // Assumes decodeRandomness is public
+ }
+}