Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 39 additions & 46 deletions src/Utils/Arrays.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,20 @@ final public function __construct()
* @param string|int $curr_key
* @return string|int|null
*/
public static function getNextKey(array &$array, string|int $curr_key): string|int|null
public static function getNextKey(array $array, string|int $curr_key): string|int|null
{
reset($array);
$next = key($array);
$keys = array_keys($array);
$position = array_search($curr_key, $keys, true);

do {
$tmp_key = key($array);
$res = next($array);
} while (($tmp_key != $curr_key) && $res);
if ($position === false) {
return $keys[0] ?? null;
}

if ($res) {
$next = key($array);
if (isset($keys[$position + 1])) {
return $keys[$position + 1];
}

return $next;
return $keys[0] ?? null;
}


Expand All @@ -46,21 +45,20 @@ public static function getNextKey(array &$array, string|int $curr_key): string|i
* @param string|int $curr_key
* @return string|int|null
*/
public static function getPreviousKey(array &$array, string|int $curr_key): string|int|null
public static function getPreviousKey(array $array, string|int $curr_key): string|int|null
{
end($array);
$prev = key($array);
$keys = array_keys($array);
$position = array_search($curr_key, $keys, true);

do {
$tmp_key = key($array);
$res = prev($array);
} while (($tmp_key != $curr_key) && $res);
if ($position === false) {
return $keys[count($keys) - 1] ?? null;
}

if ($res) {
$prev = key($array);
if (isset($keys[$position - 1])) {
return $keys[$position - 1];
}

return $prev;
return $keys[count($keys) - 1] ?? null;
}


Expand All @@ -69,24 +67,20 @@ public static function getPreviousKey(array &$array, string|int $curr_key): stri
* @param mixed $curr_val
* @return mixed
*/
public static function getNextValue(array &$array, mixed $curr_val): mixed
public static function getNextValue(array $array, mixed $curr_val): mixed
{
reset($array);

do {
$tmp_val = current($array);
$res = next($array);
} while (($tmp_val != $curr_val) && $res);

if ($res) {
$next = current($array);
} else {
// return first value
reset($array);
$next = current($array);
$values = array_values($array);
$position = array_search($curr_val, $values, true);

if ($position === false) {
return $values[0] ?? null;
}

return $next;
if (isset($values[$position + 1])) {
return $values[$position + 1];
}

return $values[0] ?? null;
}


Expand All @@ -95,21 +89,20 @@ public static function getNextValue(array &$array, mixed $curr_val): mixed
* @param mixed $curr_val
* @return mixed
*/
public static function getPreviousValue(array &$array, mixed $curr_val): mixed
public static function getPreviousValue(array $array, mixed $curr_val): mixed
{
end($array);
$prev = current($array);
$values = array_values($array);
$position = array_search($curr_val, $values, true);

do {
$tmp_val = current($array);
$res = prev($array);
} while (($tmp_val != $curr_val) && $res);
if ($position === false) {
return $values[count($values) - 1] ?? null;
}

if ($res) {
$prev = current($array);
if (isset($values[$position - 1])) {
return $values[$position - 1];
}

return $prev;
return $values[count($values) - 1] ?? null;
}

}
}
34 changes: 27 additions & 7 deletions src/Utils/Colors.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,35 @@ public static function rgbToHex(array|string $rgb, bool $withHash = true): strin
}

// Convert RGB to HEX
$hex[0] = dechex($rgb[0]);
$hex[1] = dechex($rgb[1]);
$hex[2] = dechex($rgb[2]);
$hex = sprintf("%02x%02x%02x", $rgb[0], $rgb[1], $rgb[2]);

if ($withHash) {
return strtoupper('#' . sprintf("%02s%02s%02s", $hex[0], $hex[1], $hex[2]));
return strtoupper('#' . $hex);
}

} else {
return strtoupper(sprintf("%02s%02s%02s", $hex[0], $hex[1], $hex[2]));
return strtoupper($hex);
}

/**
* @param string $hex
* @return array
*/
public static function hexToRgb(string $hex): array
{
$hex = ltrim($hex, '#');

if (strlen($hex) === 3) {
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
}

if (strlen($hex) !== 6 || !ctype_xdigit($hex)) {
throw new InvalidArgumentException("Invalid HEX color");
}

return [
'r' => hexdec(substr($hex, 0, 2)),
'g' => hexdec(substr($hex, 2, 2)),
'b' => hexdec(substr($hex, 4, 2)),
];
}
}
}
76 changes: 76 additions & 0 deletions tests/ArraysTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

namespace Aprila\Tests;

use Aprila\Utils\Arrays;
use Tester\Assert;
use Tester\TestCase;

require __DIR__ . '/../vendor/autoload.php';

class ArraysTest extends TestCase
{
public function testGetNextKey()
{
$arr = ['a' => 1, 'b' => 2, 'c' => 3];

Assert::same('b', Arrays::getNextKey($arr, 'a'));
Assert::same('c', Arrays::getNextKey($arr, 'b'));
Assert::same('a', Arrays::getNextKey($arr, 'c')); // Wrap around
Assert::same('a', Arrays::getNextKey($arr, 'non-existent')); // Not found -> first
}

public function testGetPreviousKey()
{
$arr = ['a' => 1, 'b' => 2, 'c' => 3];

Assert::same('c', Arrays::getPreviousKey($arr, 'a')); // Wrap around
Assert::same('a', Arrays::getPreviousKey($arr, 'b'));
Assert::same('b', Arrays::getPreviousKey($arr, 'c'));
Assert::same('c', Arrays::getPreviousKey($arr, 'non-existent')); // Not found -> last
}

public function testGetNextValue()
{
$arr = ['a' => 1, 'b' => 2, 'c' => 3];

Assert::same(2, Arrays::getNextValue($arr, 1));
Assert::same(3, Arrays::getNextValue($arr, 2));
Assert::same(1, Arrays::getNextValue($arr, 3)); // Wrap around
Assert::same(1, Arrays::getNextValue($arr, 999)); // Not found -> first
}

public function testGetPreviousValue()
{
$arr = ['a' => 1, 'b' => 2, 'c' => 3];

Assert::same(3, Arrays::getPreviousValue($arr, 1)); // Wrap around
Assert::same(1, Arrays::getPreviousValue($arr, 2));
Assert::same(2, Arrays::getPreviousValue($arr, 3));
Assert::same(3, Arrays::getPreviousValue($arr, 999)); // Not found -> last
}

public function testEmptyArray()
{
$arr = [];
Assert::null(Arrays::getNextKey($arr, 'a'));
Assert::null(Arrays::getPreviousKey($arr, 'a'));
Assert::null(Arrays::getNextValue($arr, 1));
Assert::null(Arrays::getPreviousValue($arr, 1));
}

public function testNumericKeys()
{
$arr = [10 => 'val1', 20 => 'val2'];
Assert::same(20, Arrays::getNextKey($arr, 10));
Assert::same(10, Arrays::getNextKey($arr, 20));
Assert::same(10, Arrays::getNextKey($arr, 999));

// Strict comparison: "10" !== 10, so key not found, returns first key
Assert::same(10, Arrays::getNextKey($arr, "10"));
}
}

(new ArraysTest())->run();
49 changes: 49 additions & 0 deletions tests/ColorsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace Aprila\Tests;

use Aprila\Utils\Colors;
use Tester\Assert;
use Tester\TestCase;

require __DIR__ . '/../vendor/autoload.php';

class ColorsTest extends TestCase
{
public function testRgbToHex()
{
Assert::same('#FFFFFF', Colors::rgbToHex([255, 255, 255]));
Assert::same('#000000', Colors::rgbToHex([0, 0, 0]));
Assert::same('#FF0000', Colors::rgbToHex([255, 0, 0]));

Assert::same('FFFFFF', Colors::rgbToHex([255, 255, 255], false));

Assert::same('#FFFFFF', Colors::rgbToHex(['r' => 255, 'g' => 255, 'b' => 255]));

Assert::same('#FFFFFF', Colors::rgbToHex('255, 255, 255'));
Assert::same('#FFFFFF', Colors::rgbToHex('rgb(255, 255, 255)'));
}

public function testHexToRgb()
{
Assert::same(['r' => 255, 'g' => 255, 'b' => 255], Colors::hexToRgb('#FFFFFF'));
Assert::same(['r' => 255, 'g' => 255, 'b' => 255], Colors::hexToRgb('FFFFFF'));
Assert::same(['r' => 0, 'g' => 0, 'b' => 0], Colors::hexToRgb('#000'));
Assert::same(['r' => 255, 'g' => 0, 'b' => 0], Colors::hexToRgb('#F00'));
}

public function testExceptions()
{
Assert::exception(function() {
Colors::rgbToHex([300, 0, 0]);
}, \InvalidArgumentException::class);

Assert::exception(function() {
Colors::hexToRgb('ZZZZZZ');
}, \InvalidArgumentException::class);
}
}

(new ColorsTest())->run();
19 changes: 9 additions & 10 deletions tests/Utils/Arrays.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ Assert::same(['d', '5'], Arrays::getPreviousValue($testNestedArray, ['f', '7']))
$emptyArray = [];
Assert::null(Arrays::getNextKey($emptyArray, 'nonexistent'));
Assert::null(Arrays::getPreviousKey($emptyArray, 'nonexistent'));
Assert::same(false, Arrays::getNextValue($emptyArray, 'nonexistent')); // returns false for empty array (current() behavior)
Assert::same(false, Arrays::getPreviousValue($emptyArray, 'nonexistent'));
Assert::null(Arrays::getNextValue($emptyArray, 'nonexistent')); // returns null for empty array
Assert::null(Arrays::getPreviousValue($emptyArray, 'nonexistent')); // returns null for empty array

// Single element array
$singleArray = ['only' => 'value'];
Expand Down Expand Up @@ -96,12 +96,11 @@ Assert::same('b', Arrays::getNextKey($nullArray, 'a'));
Assert::same('value', Arrays::getNextValue($nullArray, null)); // finds first null, returns next value
Assert::same(null, Arrays::getPreviousValue($nullArray, 'value')); // returns previous null

// Test with boolean and numeric values
// Note: Method has a bug - it treats falsy next() results as "end of array" and wraps around
// Test with boolean and numeric values (strict comparison)
$mixedValues = [true, false, 0, 1, '', 'string'];
Assert::same(true, Arrays::getNextValue($mixedValues, true)); // finds true, next() returns false (falsy), wraps to first
Assert::same(true, Arrays::getNextValue($mixedValues, false)); // finds false, next() returns 0 (falsy), wraps to first
Assert::same(true, Arrays::getNextValue($mixedValues, 0)); // finds 0, next() returns 1 (truthy), but then 1 != 0 fails next iteration
Assert::same(true, Arrays::getNextValue($mixedValues, 1)); // similar issue with truthy/falsy logic
Assert::same(true, Arrays::getNextValue($mixedValues, '')); // empty string causes similar issue
Assert::same(true, Arrays::getNextValue($mixedValues, 'string')); // wraps around
Assert::same(false, Arrays::getNextValue($mixedValues, true)); // finds true at 0, returns false at 1
Assert::same(0, Arrays::getNextValue($mixedValues, false)); // finds false at 1, returns 0 at 2
Assert::same(1, Arrays::getNextValue($mixedValues, 0)); // finds 0 at 2, returns 1 at 3
Assert::same('', Arrays::getNextValue($mixedValues, 1)); // finds 1 at 3, returns '' at 4
Assert::same('string', Arrays::getNextValue($mixedValues, '')); // finds '' at 4, returns 'string' at 5
Assert::same(true, Arrays::getNextValue($mixedValues, 'string')); // finds 'string' at 5, wraps to true at 0