Skip to content

Commit f1477b1

Browse files
committed
Allow Replace of Dummy Function with Custom Function
Custom functions were introduced recently, with a restriction that non-custom functions could not be replaced. However, reviewing a recent issue has led me to the conclusion that it might sometimes be impractical to implement an Excel function in PhpSpreadsheet for all users, but it could still be helpful to some users to offer an implementation anyhow. The Excel ASC function is currently unimplemented in PhpSpreadsheet, and is a candidate for replacement using the functionality added in this PR. This PR supersedes PR #4513, which I will now close, and its earlier incarnation PR #4511. For reasons discussed in 4513, I don't see a way forward for implementing the Excel ASC function in a way that would be generally usable. However, this PR would permit a user to implement ASC in a way which would satisfy the user's local requirements. See the new `testReplaceDummyFunction`; I think, or at least hope, that, despite the deficiencies of 4511 and 4513, this might satisfy the author's requirement.
1 parent 747ccd1 commit f1477b1

File tree

3 files changed

+55
-1
lines changed

3 files changed

+55
-1
lines changed

src/PhpSpreadsheet/Calculation/CalculationBase.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ protected static function &getFunctionsAddress(): array
3030
public static function addFunction(string $key, array $value): bool
3131
{
3232
$key = strtoupper($key);
33-
if (array_key_exists($key, FunctionArray::$phpSpreadsheetFunctions)) {
33+
if (
34+
array_key_exists($key, FunctionArray::$phpSpreadsheetFunctions)
35+
&& !self::isDummy($key)
36+
) {
3437
return false;
3538
}
3639
$value['custom'] = true;
@@ -39,6 +42,20 @@ public static function addFunction(string $key, array $value): bool
3942
return true;
4043
}
4144

45+
private static function isDummy(string $key): bool
46+
{
47+
// key is already known to exist
48+
$functionCall = FunctionArray::$phpSpreadsheetFunctions[$key]['functionCall'] ?? null;
49+
if (!is_array($functionCall)) {
50+
return false;
51+
}
52+
if (($functionCall[1] ?? '') !== 'DUMMY') {
53+
return false;
54+
}
55+
56+
return true;
57+
}
58+
4259
public static function removeFunction(string $key): bool
4360
{
4461
$key = strtoupper($key);

tests/PhpSpreadsheetTests/Calculation/CustomFunction.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcException;
88
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Helpers;
9+
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
910

1011
class CustomFunction
1112
{
@@ -19,4 +20,25 @@ public static function fourthPower(mixed $number): float|int|string
1920

2021
return $number ** 4;
2122
}
23+
24+
/**
25+
* ASC.
26+
* Converts full-width (double-byte) characters to half-width (single-byte) characters.
27+
* There are many difficulties with implementing this into PhpSpreadsheet.
28+
*/
29+
public static function ASC(mixed $stringValue): string
30+
{
31+
/*if (is_array($stringValue)) {
32+
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $stringValue);
33+
}*/
34+
$stringValue = StringHelper::convertToString($stringValue, convertBool: true);
35+
if (function_exists('mb_convert_kana')) {
36+
return mb_convert_kana($stringValue, 'a', 'UTF-8');
37+
}
38+
// Fallback if mb_convert_kana is not available.
39+
// PhpSpreadsheet heavily relies on mbstring, so this is more of a theoretical fallback.
40+
// A comprehensive manual conversion is extensive.
41+
42+
return $stringValue;
43+
}
2244
}

tests/PhpSpreadsheetTests/Calculation/CustomFunctionTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,19 @@ public static function testCustomFunction(): void
3131
self::assertSame('#NAME?', $calculation->calculateFormula('=FOURTHPOWER(3)'));
3232
self::assertFalse(Calculation::removeFunction('WHATEVER'));
3333
}
34+
35+
public static function testReplaceDummyFunction(): void
36+
{
37+
$functions = Calculation::getFunctions();
38+
$key = 'ASC';
39+
$oldValue = $functions[$key] ?? null;
40+
self::assertIsArray($oldValue);
41+
$calculation = Calculation::getInstance();
42+
$value = $oldValue;
43+
$value['functionCall'] = [CustomFunction::class, 'ASC'];
44+
self::assertTrue(Calculation::addFunction($key, $value));
45+
self::assertSame('ABC', $calculation->calculateFormula('=ASC("ABC")'));
46+
self::assertTrue(Calculation::removeFunction('ASC'));
47+
self::assertTrue(Calculation::addFunction($key, $oldValue));
48+
}
3449
}

0 commit comments

Comments
 (0)