ArrayEnabled.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet\Calculation;
  3. use PhpOffice\PhpSpreadsheet\Calculation\Engine\ArrayArgumentHelper;
  4. use PhpOffice\PhpSpreadsheet\Calculation\Engine\ArrayArgumentProcessor;
  5. trait ArrayEnabled
  6. {
  7. /**
  8. * @var ArrayArgumentHelper
  9. */
  10. private static $arrayArgumentHelper;
  11. /**
  12. * @param array|false $arguments Can be changed to array for Php8.1+
  13. */
  14. private static function initialiseHelper($arguments): void
  15. {
  16. if (self::$arrayArgumentHelper === null) {
  17. self::$arrayArgumentHelper = new ArrayArgumentHelper();
  18. }
  19. self::$arrayArgumentHelper->initialise(($arguments === false) ? [] : $arguments);
  20. }
  21. /**
  22. * Handles array argument processing when the function accepts a single argument that can be an array argument.
  23. * Example use for:
  24. * DAYOFMONTH() or FACT().
  25. */
  26. protected static function evaluateSingleArgumentArray(callable $method, array $values): array
  27. {
  28. $result = [];
  29. foreach ($values as $value) {
  30. $result[] = $method($value);
  31. }
  32. return $result;
  33. }
  34. /**
  35. * Handles array argument processing when the function accepts multiple arguments,
  36. * and any of them can be an array argument.
  37. * Example use for:
  38. * ROUND() or DATE().
  39. *
  40. * @param mixed ...$arguments
  41. */
  42. protected static function evaluateArrayArguments(callable $method, ...$arguments): array
  43. {
  44. self::initialiseHelper($arguments);
  45. $arguments = self::$arrayArgumentHelper->arguments();
  46. return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
  47. }
  48. /**
  49. * Handles array argument processing when the function accepts multiple arguments,
  50. * but only the first few (up to limit) can be an array arguments.
  51. * Example use for:
  52. * NETWORKDAYS() or CONCATENATE(), where the last argument is a matrix (or a series of values) that need
  53. * to be treated as a such rather than as an array arguments.
  54. *
  55. * @param mixed ...$arguments
  56. */
  57. protected static function evaluateArrayArgumentsSubset(callable $method, int $limit, ...$arguments): array
  58. {
  59. self::initialiseHelper(array_slice($arguments, 0, $limit));
  60. $trailingArguments = array_slice($arguments, $limit);
  61. $arguments = self::$arrayArgumentHelper->arguments();
  62. $arguments = array_merge($arguments, $trailingArguments);
  63. return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
  64. }
  65. /**
  66. * @param mixed $value
  67. */
  68. private static function testFalse($value): bool
  69. {
  70. return $value === false;
  71. }
  72. /**
  73. * Handles array argument processing when the function accepts multiple arguments,
  74. * but only the last few (from start) can be an array arguments.
  75. * Example use for:
  76. * Z.TEST() or INDEX(), where the first argument 1 is a matrix that needs to be treated as a dataset
  77. * rather than as an array argument.
  78. *
  79. * @param mixed ...$arguments
  80. */
  81. protected static function evaluateArrayArgumentsSubsetFrom(callable $method, int $start, ...$arguments): array
  82. {
  83. $arrayArgumentsSubset = array_combine(
  84. range($start, count($arguments) - $start),
  85. array_slice($arguments, $start)
  86. );
  87. if (self::testFalse($arrayArgumentsSubset)) {
  88. return ['#VALUE!'];
  89. }
  90. self::initialiseHelper($arrayArgumentsSubset);
  91. $leadingArguments = array_slice($arguments, 0, $start);
  92. $arguments = self::$arrayArgumentHelper->arguments();
  93. $arguments = array_merge($leadingArguments, $arguments);
  94. return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
  95. }
  96. /**
  97. * Handles array argument processing when the function accepts multiple arguments,
  98. * and any of them can be an array argument except for the one specified by ignore.
  99. * Example use for:
  100. * HLOOKUP() and VLOOKUP(), where argument 1 is a matrix that needs to be treated as a database
  101. * rather than as an array argument.
  102. *
  103. * @param mixed ...$arguments
  104. */
  105. protected static function evaluateArrayArgumentsIgnore(callable $method, int $ignore, ...$arguments): array
  106. {
  107. $leadingArguments = array_slice($arguments, 0, $ignore);
  108. $ignoreArgument = array_slice($arguments, $ignore, 1);
  109. $trailingArguments = array_slice($arguments, $ignore + 1);
  110. self::initialiseHelper(array_merge($leadingArguments, [[null]], $trailingArguments));
  111. $arguments = self::$arrayArgumentHelper->arguments();
  112. array_splice($arguments, $ignore, 1, $ignoreArgument);
  113. return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
  114. }
  115. }