Parcourir la source

发货单打印,excel表格

qiuenguang il y a 1 an
Parent
commit
e89f23b3c6
100 fichiers modifiés avec 4781 ajouts et 1470 suppressions
  1. 231 72
      application/admin/controller/Deliver.php
  2. 6 0
      application/admin/view/deliver/dispatch.html
  3. 116 10
      public/assets/js/backend/deliver.js
  4. 133 0
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ArrayEnabled.php
  5. 181 0
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/BinaryComparison.php
  6. 246 152
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php
  7. 2 2
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DGet.php
  8. 19 19
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php
  9. 5 5
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php
  10. 19 15
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php
  11. 27 3
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php
  12. 27 21
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php
  13. 18 7
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php
  14. 18 6
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php
  15. 18 6
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php
  16. 28 6
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php
  17. 23 4
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php
  18. 19 2
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php
  19. 18 7
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php
  20. 27 7
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php
  21. 23 6
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php
  22. 38 14
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php
  23. 27 15
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php
  24. 21 8
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php
  25. 209 0
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php
  26. 175 0
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php
  27. 223 0
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/BranchPruner.php
  28. 13 11
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php
  29. 61 61
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php
  30. 14 6
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php
  31. 14 6
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
  32. 32 8
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php
  33. 31 8
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php
  34. 75 27
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php
  35. 27 15
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php
  36. 36 14
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php
  37. 184 86
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php
  38. 28 14
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php
  39. 9 5
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php
  40. 44 15
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php
  41. 50 20
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php
  42. 45 16
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php
  43. 45 16
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php
  44. 21 11
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
  45. 3 3
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php
  46. 20 6
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Erf.php
  47. 13 4
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php
  48. 3 1
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ExceptionHandler.php
  49. 6 6
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php
  50. 11 7
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Amortization.php
  51. 2 2
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php
  52. 4 3
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic.php
  53. 3 2
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php
  54. 6 5
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
  55. 2 1
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php
  56. 3 2
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php
  57. 36 21
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php
  58. 7 6
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php
  59. 6 4
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Coupons.php
  60. 10 9
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php
  61. 54 19
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Dollar.php
  62. 11 11
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php
  63. 3 3
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Helpers.php
  64. 3 2
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php
  65. 3 3
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php
  66. 7 6
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
  67. 5 4
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php
  68. 3 3
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php
  69. 4 4
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php
  70. 10 9
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php
  71. 11 11
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php
  72. 7 7
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php
  73. 290 290
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php
  74. 71 0
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php
  75. 171 0
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ExcelError.php
  76. 328 0
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/Value.php
  77. 4 4
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/WildcardMatch.php
  78. 1 1
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical.php
  79. 42 16
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Conditional.php
  80. 16 7
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php
  81. 25 25
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php
  82. 35 9
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Address.php
  83. 15 10
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
  84. 81 0
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Filter.php
  85. 8 8
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php
  86. 18 13
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php
  87. 9 9
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php
  88. 7 6
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php
  89. 41 9
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php
  90. 8 3
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php
  91. 25 7
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
  92. 5 4
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php
  93. 33 5
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php
  94. 22 10
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
  95. 25 25
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php
  96. 12 7
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php
  97. 342 0
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php
  98. 141 0
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Unique.php
  99. 26 14
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
  100. 98 98
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php

+ 231 - 72
application/admin/controller/Deliver.php

@@ -10,6 +10,7 @@ use app\admin\model\QcodeCompany;
 use app\admin\model\QcodeBach;
 use app\admin\model\QcodeExport;
 use fast\Arr;
+use PhpOffice\PhpSpreadsheet\IOFactory;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
 use PhpOffice\PhpSpreadsheet\Style\Alignment;
 use PhpOffice\PhpSpreadsheet\Exception;
@@ -54,7 +55,7 @@ class Deliver extends Backend
         // 接收参数
         $userInfo = Session::get('admin');
         $where = [
-            'delete_time'=> ''
+            'delete_time'=> '',
         ];
         $filter = input('filter');
         $filter = json_decode($filter,true);
@@ -89,6 +90,7 @@ class Deliver extends Backend
         $large_list = $large->name($userInfo['company'].'_'.'qcode_large')
             ->order('creat_time',$order)
             ->where($where)
+            ->where('l_status',0)
             ->whereIn('bach_id',$bach_id)
             ->skip($offset)
             ->limit($limit)
@@ -197,7 +199,6 @@ class Deliver extends Backend
                 }
 
             }
-
             $large_str = substr($large_str,0,strlen($large_str)-1);
             foreach (explode(',',$large_str) as $kk=>$vv){
                 $n = $n + $small->name($userinfo['company'].'_'.'qcode_small')->where('large_id',$vv)->count();
@@ -208,7 +209,8 @@ class Deliver extends Backend
             }else{
                 $mater_type = 1;
             }
-//            $file_dir = $this->exportExcel($v,$large_str,$userinfo['company'],$m,$n);
+            $exportData = $this->exportdata($v,$large_str,$userinfo['company'],$m,$n);
+            $file_dir = $this->exportExcel($exportData);
             $data[$k] = [
                 'username'  => $bachDetail['supplier_name'],
                 'matter_name' => $bachDetail['matter_name'],
@@ -223,28 +225,30 @@ class Deliver extends Backend
                 'note' => '',
                 'user_id' => $userinfo['id'],
                 'create_time' => time(),
-                'file_dir' => '',
+                'file_dir' => $file_dir,
                 'company_id' => $userinfo['company'],
             ];
             $res = $export->save($data[$k]);
+            if ($res === false){
+                $this->error('发货失败');
+            }
+            $lager->name($userinfo['company'].'_'.'qcode_large')->where('bach_id',$v)->whereIn('_id',$lagerId)->update(['l_status'=>1]);
         }
         $this->success();
     }
-
     /**
-     * 生成excel
+     * 表格数据处理
      * @param $bach_id
      * @param $large_str
      * @param $company
      * @param $large_num
      * @param $small_num
-     * @return string
-     * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
+     * @return array
      * @throws \think\db\exception\DataNotFoundException
      * @throws \think\db\exception\ModelNotFoundException
      * @throws \think\exception\DbException
      */
-    public function exportExcel($bach_id,$large_str,$company,$large_num,$small_num)
+    public function exportdata($bach_id,$large_str,$company,$large_num,$small_num)
     {
         $bach = new QcodeBach();
         $large = new QcodeLarge();
@@ -281,77 +285,93 @@ class Deliver extends Backend
                 $data['code'][] = $code;
             }
         }
+        return $data;
+    }
+
+    /**
+     * 生成excel
+     * @param $data
+     * @return string
+     * @throws Exception
+     * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
+     */
+    public function exportExcel($data)
+    {
         $objexcel = new Spreadsheet();
         $sheet = $objexcel->getActiveSheet();
         //设置全局单元格垂直居中
-//        $objexcel->getDefaultStyle()->getAlignment()->setVertical(Alignment::HORIZONTAL_CENTER);
-//        //设置全局单元格水平居中
-//        $objexcel->getDefaultStyle()->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
-//        //设置全局单元格自动换行
-//        $objexcel->getDefaultStyle()->getAlignment()->setWrapText(true);
+        $objexcel->getDefaultStyle()->getAlignment()->setVertical(Alignment::HORIZONTAL_CENTER);
+        //设置全局单元格水平居中
+        $objexcel->getDefaultStyle()->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
+        //设置全局单元格自动换行
+        $objexcel->getDefaultStyle()->getAlignment()->setWrapText(true);
         //设置单元格宽度
-        $sheet->getDefaultColumnDimension()->setWidth(25);
+        $sheet->getDefaultColumnDimension()->setWidth(50);
         //设置表头
-//        $sheet->setCellValue('A1', '文件流水号');
-//        $sheet->setCellValue('A2', '订单号');
-//        $sheet->setCellValue('A3', '供应商');
-//        $sheet->setCellValue('A4', '供应商代码');
-//        $sheet->setCellValue('A5', '物料名称');
-//        $sheet->setCellValue('A6', '物料代码');
-//        $sheet->setCellValue('A7', '生产日期');
-//        $sheet->setCellValue('A8', '打码日期');
-//        $sheet->setCellValue('A9', '大件数量');
-//        $sheet->setCellValue('A10', '托盘小件数量');
-//        $sheet->setCellValue('A11', '小件总数量');
-//        $sheet->setCellValue('C3', '大件预留码');
-//        $sheet->setCellValue('C5', '小件预留码');
-//        $sheet->setCellValue('C7', '大件开始流水号');
-//        $sheet->setCellValue('C9', '小件开始流水号');
-//        $sheet->setCellValue('C11', '大件重量(Kg)');
-//        $sheet->setCellValue('C13', '小件重量(Kg)');
-//        $sheet->setCellValue('A15', '大件二维码');
-//        $sheet->setCellValue('B15', '大件条码(预留)');
-//        $sheet->setCellValue('C15', '小件二维码');
-//        $sheet->setCellValue('D15', '小件条码(预留)');
+        $sheet->setCellValue('A1', '文件流水号');
+        $sheet->setCellValue('A2', '订单号');
+        $sheet->setCellValue('A3', '供应商');
+        $sheet->setCellValue('A4', '供应商代码');
+        $sheet->setCellValue('A5', '物料名称');
+        $sheet->setCellValue('A6', '物料代码');
+        $sheet->setCellValue('A7', '生产日期');
+        $sheet->setCellValue('A8', '打码日期');
+        $sheet->setCellValue('A9', '大件数量');
+        $sheet->setCellValue('A10', '托盘小件数量');
+        $sheet->setCellValue('A11', '小件总数量');
+        $sheet->setCellValue('C3', '大件预留码');
+        $sheet->setCellValue('C5', '小件预留码');
+        $sheet->setCellValue('C7', '大件开始流水号');
+        $sheet->setCellValue('C9', '小件开始流水号');
+        $sheet->setCellValue('C11', '大件重量(Kg)');
+        $sheet->setCellValue('C13', '小件重量(Kg)');
+        $sheet->setCellValue('A15', '大件二维码');
+        $sheet->setCellValue('B15', '大件条码(预留)');
+        $sheet->setCellValue('C15', '小件二维码');
+        $sheet->setCellValue('D15', '小件条码(预留)');
         //插入表格数据
-//        $sheet->getCell('B3')->setValue(isset($data['supplier_name'])?$data['supplier_name']:'');
-//        $sheet->getCell('B4')->setValue(isset($data['supplier_no'])?$data['supplier_no']:'');
-//        $sheet->getCell('B5')->setValue(isset($data['matter_name'])?$data['matter_name']:'');
-//        $sheet->getCell('B6')->setValue(isset($data['matter_no'])?$data['matter_no']:'');
-//        $sheet->getCell('B7')->setValue(isset($data['manufacture_date'])?$data['manufacture_date']:'');
-//        $sheet->getCell('B8')->setValue(isset($data['print_date'])?$data['print_date']:'');
-//        $sheet->getCell('B9')->setValue(isset($data['box_num'])?$data['box_num']:'');
-//        $sheet->getCell('B10')->setValue(isset($data['tray_num'])?$data['tray_num']:'');
-//        $sheet->getCell('B11')->setValue(isset($data['small_num'])?$data['small_num']:'');
-//        $sheet->getCell('C4')->setValue(isset($data['l_reservation'])?$data['l_reservation']:'');
-//        $sheet->getCell('C6')->setValue(isset($data['s_reservation'])?$data['s_reservation']:'');
-//        $sheet->getCell('C8')->setValue(isset($data['l_flow'])?$data['l_flow']:'');
-//        $sheet->getCell('C10')->setValue(isset($data['s_flow'])?$data['s_flow']:'');
-//        $sheet->getCell('C12')->setValue(isset($data['l_weight'])?$data['l_weight']:'');
-//        $sheet->getCell('C14')->setValue(isset($data['s_flow'])?$data['s_flow']:'');
-//        foreach ($data['code'] as $v){
-//            $sheet->getCell('A16')->setValue(isset($v['large_code'])?$v['large_code']:'');
-//            $sheet->getCell('B16')->setValue('');
-//            $sheet->getCell('C16')->setValue(isset($v['small_code'])?$v['small_code']:'');
-//            $sheet->getCell('D16')->setValue('');
-//        }
+        $sheet->getCell('B3')->setValue(isset($data['supplier_name'])?$data['supplier_name']:'');
+        $sheet->getCell('B4')->setValue(isset($data['supplier_no'])?$data['supplier_no']:'');
+        $sheet->getCell('B5')->setValue(isset($data['matter_name'])?$data['matter_name']:'');
+        $sheet->getCell('B6')->setValue(isset($data['matter_no'])?$data['matter_no']:'');
+        $sheet->getCell('B7')->setValue(isset($data['manufacture_date'])?$data['manufacture_date']:'');
+        $sheet->getCell('B8')->setValue(isset($data['print_date'])?$data['print_date']:'');
+        $sheet->getCell('B9')->setValue(isset($data['box_num'])?$data['box_num']:'');
+        $sheet->getCell('B10')->setValue(isset($data['tray_num'])?$data['tray_num']:'');
+        $sheet->getCell('B11')->setValue(isset($data['small_num'])?$data['small_num']:'');
+        $sheet->getCell('C4')->setValue(isset($data['l_reservation'])?$data['l_reservation']:'');
+        $sheet->getCell('C6')->setValue(isset($data['s_reservation'])?$data['s_reservation']:'');
+        $sheet->getCell('C8')->setValue(isset($data['l_flow'])?$data['l_flow']:'');
+        $sheet->getCell('C10')->setValue(isset($data['s_flow'])?$data['s_flow']:'');
+        $sheet->getCell('C12')->setValue(isset($data['l_weight'])?$data['l_weight']:'');
+        $sheet->getCell('C14')->setValue(isset($data['s_weight'])?$data['s_weight']:'');
+        foreach ($data['code'] as $v){
+            $sheet->getCell('A16')->setValue(isset($v['large_code'])?$v['large_code']:'');
+            $sheet->getCell('B16')->setValue('');
+            $sheet->getCell('C16')->setValue(isset($v['small_code'])?$v['small_code']:'');
+            $sheet->getCell('D16')->setValue('');
+        }
         //文件另存
-        $filename = date('Ymd',time()).rand(100,999).'_'.$data['supplier_name'].'_'.$data['matter_name'].'_'.$data['small_num'].'.xlsx';
+        $filename = date('Ymd',time()).rand(100,999).'_'.$data['supplier_name'].'_'.str_replace('/','_',$data['matter_name']).'_'.$data['small_num'].'.xlsx';
 //        $filename = iconv("utf-8","gb2312",$filename);
         header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
-        header('Content-Disposition: attachment;filename="'.$filename.'"');
+        header('Content-Disposition:attachment;filename='.$filename);
         header('Cache-Control: max-age=0');
         header("Content-Type:text/html;charset=utf-8");
-//        if(is_dir(ROOT_PATH.'public/uploads/export/') == null)
-//        {
-//            mkdir(ROOT_PATH.'public/uploads/export/',0777,true);
-//        }
-        $writer = new Xlsx($objexcel);
-//        $writer->save($filename);
-        $writer->save(ROOT_PATH.'public/uploads/export'.DS.$filename);
+        if(is_dir(ROOT_PATH.'public/uploads/export/') === null)
+        {
+            mkdir(ROOT_PATH.'public/uploads/export/',0777,true);
+        }
+        $writer = IOFactory::createWriter($objexcel, 'Xlsx');
+        $writer->save(ROOT_PATH.'public/uploads/export/'.$filename);
         return 'public/uploads/export/'.$filename;
     }
-    //批次发货审批
+
+    /**
+     * 批次发货申请
+     * @return string|\think\response\Json
+     * @throws \think\Exception
+     */
     public function apply()
     {
         $export = new QcodeExport();
@@ -413,7 +433,12 @@ class Deliver extends Backend
         $result = ['total'=>$total,'rows'=>$list];
         return json($result);
     }
-    //批次发货申请删除
+
+    /**
+     * 批次发货删除
+     * @param $ids
+     * @return void
+     */
     public function apply_del($ids)
     {
         $export = new QcodeExport();
@@ -426,12 +451,26 @@ class Deliver extends Backend
         }
         $this->success('成功');
     }
+
+    /**
+     * 司机弹窗
+     * @param $ids
+     * @return string
+     * @throws \think\Exception
+     */
     public function goods($ids)
     {
         $this->view->assign('ids',$ids);
         return $this->view->fetch();
     }
-    //批次发货提交
+
+    /**
+     * 批次发货提交
+     * @return void
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \think\exception\DbException
+     */
     public function apply_add()
     {
         $goods = new QcodeGoods();
@@ -483,7 +522,12 @@ class Deliver extends Backend
             $this->error('失败');
         }
     }
-    //生成二维码
+
+    /**
+     * 生成二维码
+     * @param $url
+     * @return string
+     */
     public function Qrcode($url)
     {
         $level=2;
@@ -503,10 +547,125 @@ class Deliver extends Backend
         //把生成的base64字符串返回给前端
         return 'data:image/png;base64,'.$imageString;
     }
-    //发货单
+
+    /**
+     * 发货单
+     * @return string|\think\response\Json
+     * @throws \think\Exception
+     */
     public function dispatch()
     {
-        return $this->view->fetch();
+        $export = new QcodeGoods();
+        $this->request->filter(['strip_tags', 'trim']);
+        if (false === $this->request->isAjax()) {
+            return $this->view->fetch();
+        }
+        if ($this->request->request('keyField')) {
+            return $this->selectpage();
+        }
+        // 接收参数
+        $userInfo = Session::get('admin');
+        $where = [
+            'company_id' => $userInfo['company'],
+            'delete_time'=> ''
+        ];
+        $search = input('search');
+        if (isset($search)){
+            $where['order_number|supplier_name|status|plate_number|shdh|deliveryman'] = new \MongoDB\BSON\Regex($search);
+        }
+        $filter = input('filter');
+        $filter = json_decode($filter,true);
+        //订单编号查询
+        if (isset($filter['order_number'])){
+            $where['order_number'] =$filter['order_number'];
+        }
+        //所属公司查询
+        if (isset($filter['supplier_name'])){
+            $where['supplier_name'] = $filter['supplier_name'];
+        }
+        if (isset($filter['status'])){
+            $where['status'] = (int)$filter['status'];
+        }
+        if (isset($filter['create_time'])){
+            $begin = substr($filter['create_time'],0,19);
+            $end = substr($filter['create_time'],22);
+            $begin = strtotime($begin);
+            $end = strtotime($end);
+            $where['create_time'] = ['between',[$begin,$end]];
+        }
+        $order = input('order');
+        $offset = input('offset');
+        $limit = input('limit');
+        $total = $export->where($where)->count();
+        $export_list = $export
+            ->order('_id',$order)
+            ->where($where)
+            ->whereIn('status',[0,1])
+            ->skip($offset)
+            ->limit($limit)
+            ->select();
+        $list=[];
+        foreach ($export_list as $k=>$v) {
+            $list[$k]['id'] = substr(json_encode($v['_id']),9,-2);
+            $list[$k]['shdh'] = $v['shdh'];
+            $list[$k]['order_number'] = $v['order_number'];
+            $list[$k]['supplier_name'] = $v['supplier_name'];
+            $list[$k]['deliveryman'] = $v['deliveryman'];
+            $list[$k]['plate_number'] = $v['plate_number'];
+            $list[$k]['create_time'] = $v['create_time'];
+            $list[$k]['status'] = $v['status'];
+        }
+        $result = ['total'=>$total,'rows'=>$list];
+        return json($result);
+    }
+
+    /**
+     * 发货单打印
+     * @return \think\response\Json|void
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \think\exception\DbException
+     */
+    public function printqrcode()
+    {
+        $goods = new QcodeGoods();
+        $export = new QcodeExport();
+        $large = new QcodeLarge();
+        $userinfo = Session::get('admin');
+        if ($this->request->isAjax() === false) {
+            $this->error('请求错误');
+        }
+        $id = input('id');
+        $good_list = $goods->where('_id',$id)->find();
+        if ($good_list){
+            if (strpos($good_list['export_id'],',') === false){
+                $export_id[0] = $good_list['export_id'];
+            }else{
+                $export_id = explode(',',$good_list['export_id']);
+            }
+            $data = [];
+            foreach ($export_id as $k=>$v){
+                $data[$k] = $export->where('_id',$v)->find();
+                $large_str = explode(',',$data[$k]['large_str']);
+                $large_list = $large->whereIn('_id',$large_str)->select();
+                $large_num = $large_weight = 0;
+                foreach ($large_list as $value){
+                    $large_num = $large_num + $value['l_num'];
+                    $large_weight = $large_weight + $value['l_weight'];
+                }
+                $data[$k]['l_num'] = $large_num/10000;
+                $data[$k]['l_weight'] = $large_weight/100;
+            }
+            $good_list['address'] = $userinfo['company_address'];
+            $good_list['data'] = $data;
+            $good_list['count'] = count($data);
+            $good_list['shrq_date'] = substr($good_list['create_time'],0,10) ;
+            if (empty($good_list['qrcode_add'])){
+                $good_list['qrcode_add'] = $this->Qrcode($good_list['shdh']);
+                $goods->where('_id',$id)->update(['qrcode_add'=>$good_list['qrcode_add']]);
+            }
+            return json(['code'=>1,'data'=>$good_list]);
+        }
     }
 
     /**

+ 6 - 0
application/admin/view/deliver/dispatch.html

@@ -1,3 +1,4 @@
+
 <div class="panel panel-default panel-intro">
     <div class="panel-body">
         <div id="myTabContent" class="tab-content">
@@ -19,3 +20,8 @@
         </div>
     </div>
 </div>
+<div class="table" style="margin:0px 30px;">
+    <div id="printcode">
+
+    </div>
+</div>

+ 116 - 10
public/assets/js/backend/deliver.js

@@ -49,6 +49,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                       alert('至少选择一个大件');
                   }
                   var lager = ids.toString();
+                  // window.location.href = 'print?lager_id='+lager;
                   Fast.api.ajax({
                       url:'deliver/print',
                       data:{lager_id:lager},
@@ -245,22 +246,127 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                     [
                         {checkbox: true},
                         {field: 'id', title: __('Id'),visible:false,operate: false},
-                        {field: 'matter_name', title: '辅料名称', operate: 'LIKE',},
-                        {field: 'matter_no', title: '辅料编码', operate: false},
-                        {field: 'username', title: '所属用户', operate: 'LIKE'},
-                        {field: 'large_num', title: '大件数量', operate: false},
-                        {field: 'small_num', title: '小件数量', operate: false},
-                        {field: 'create_time', title: '创建时间', operate: 'RANGE', addclass: 'datetimerange', formatter: Table.api.formatter.datetime},
-                        {field: 'file_dir', title: '下载路径', operate: false},
-                        {field: 'status', title: '状态', operate: 'LIKE',searchList: {"0":'待发货',"1":'已删除'}, formatter: Table.api.formatter.status},
+                        {field: 'shdh', title: '收货单号', operate: false},
+                        {field: 'order_number', title: '订单号', operate: 'LIKE'},
+                        {field: 'deliveryman', title: '司机', operate: false},
+                        {field: 'plate_number', title: '车牌号', operate: false},
+                        {field: 'supplier_name', title: '供应商名称', operate: 'LIKE'},
+                        {field: 'create_time', title: '发货时间', operate: 'RANGE', addclass: 'datetimerange', formatter: Table.api.formatter.datetime},
+                        {field: 'buttons',
+                            width: "120px",
+                            title: __('发货单打印'),
+                            table: table,
+                            events: Table.api.events.operate,
+                            buttons: [
+                                {
+                                    name: 'ajax',
+                                    text: __('点击打印'),
+                                    title: __('点击打印'),
+                                    classname: 'btn btn-xs btn-success btn-magic btn-ajax',
+                                    icon: 'fa fa-magic',
+                                    url: 'deliver/printqrcode/id/{ids}',
+                                    // confirm: '确认发送',
+                                    success: function (data,res) {
+                                        if (res.code === 1){
+                                            var arr = res.data.data;
+                                            var note='';
+                                            var html = '<div style="width: 1100px;height: 100px;position: relative;">\n' +
+                                                '                   <div style="float: left">\n' +
+                                                '                       <div style="width: 1000px;font-weight: 400;font-size: 28px;text-align: center;line-height: 50px;" class="company">'+res.data.supplier_name+'</div>\n' +
+                                                '                       <div style="width: 1000px;font-weight: 400;font-size: 24px;text-align: center;line-height: 50px;">送货单</div>\n' +
+                                                '                   </div>\n' +
+                                                '                   <div id="qrcode" style="display:inline-block;width: 105px;height: 105px;position: absolute;right: 100px;top: -5px;">\n' +
+                                                '                       <img src="" style="width: 105px;height: 105px;" id="qrcode_image"/>\n' +
+                                                '                   </div>\n' +
+                                                '              </div>\n' +
+                                                '              <table class="tg1" style="margin-top: 3px;border-collapse:collapse;border-spacing:0;font-weight:500;width:1186px">\n' +
+                                                '                   <tr class="info">\n' +
+                                                '                       <td colspan="6" style="border:none">客户名称:河南中烟工业有限责任公司黄金叶生产制造中心</td>\n' +
+                                                '                       <td colspan="4" style="border:none">送货单号:<span style="font-size: 16px;" id="shdh">'+res.data.shdh+'</span></td>\n' +
+                                                '                   </tr>\n' +
+                                                '                   <tr class="info">\n' +
+                                                '                       <td colspan="6" style="border:none">送货地址:河南省郑州市经开区第三大街9号</td>\n' +
+                                                '                       <td colspan="4" style="border:none">送货日期:<span style="font-size: 16px;"  id="shrq_date">'+res.data.shrq_date+'</span></td>\n' +
+                                                '                   </tr>\n' +
+                                                '               </table>\n' +
+                                                '               <br>\n' +
+                                                '               <table class="tg1" style="table-layout:fixed;width: 1186px;border-collapse:collapse;border-spacing:0;font-weight:500; position: relative;" id="table">'+
+                                                '                   <tr><th class="tg-s6z2" style="font-family:Arial, sans-serif;font-size:20px;text-align:center;font-weight:500;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;">物料名称</th>' +
+                                                '                   <th class="tg-s6z2" style="font-family:Arial, sans-serif;font-size:20px;text-align:center;font-weight:500;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;">生产批号</th>' +
+                                                '                   <th class="tg-s6z2" style="font-family:Arial, sans-serif;font-size:20px;text-align:center;font-weight:500;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;">大件</th>' +
+                                                '                   <th class="tg-s6z2" style="font-family:Arial, sans-serif;font-size:20px;text-align:center;font-weight:500;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;">小件</th>' +
+                                                '                   <th class="tg-s6z2" style="font-family:Arial, sans-serif;font-size:20px;text-align:center;font-weight:500;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;">单位</th>' +
+                                                '                   <th class="tg-s6z2" style="font-family:Arial, sans-serif;font-size:20px;text-align:center;font-weight:500;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;">实发数量</th>' +
+                                                '                   <th class="tg-s6z2" style="font-family:Arial, sans-serif;font-size:20px;text-align:center;font-weight:500;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;">备注</th>' +
+                                                '                   <th id="explain" rowspan="'+(res.data.count+1)+'" width="7%" style="border: none"><span style="writing-mode: tb-rl;height: 300px;font-size:16px;text-align: center;">' +
+                                                '                   蓝联(回):业务 黄联(回):运输 '+'<br>'+'白联:存根 红联:财务 绿联:客户</span></th></tr>';
+                                                                for (var i=0;i < arr.length;i++){
+                                                                    html+= '<tr><td class="tg-031e" style="font-family:Arial, sans-serif;font-size:18px;text-align:center;border-style:solid;border-width:1px;overflow:hidden;word-break:break-all;border-color:black;">'+arr[i].matter_name+'</td>';
+                                                                    html+= '<td class="tg-031e" style="font-family:Arial, sans-serif;font-size:18px;text-align:center;border-style:solid;border-width:1px;overflow:hidden;word-break:break-all;border-color:black;">'+(arr[i].bach_num?arr[i].bach_num:'')+'</td>';
+                                                                    html+= '<td class="tg-031e" style="font-family:Arial, sans-serif;font-size:18px;text-align:center;border-style:solid;border-width:1px;overflow:hidden;word-break:break-all;border-color:black;">'+arr[i].large_num+'</td>';
+                                                                    html+= '<td class="tg-031e" style="font-family:Arial, sans-serif;font-size:18px;text-align:center;border-style:solid;border-width:1px;overflow:hidden;word-break:break-all;border-color:black;">'+arr[i].small_num+'</td>';
+                                                                    if(arr[i].mater_type==1){
+                                                                        html+= '<td class="tg-031e" style="font-family:Arial, sans-serif;font-size:18px;text-align:center;border-style:solid;border-width:1px;overflow:hidden;word-break:break-all;border-color:black;">万张</td>';
+                                                                        html+='<td class="tg-031e" style="font-family:Arial, sans-serif;font-size:18px;text-align:center;border-style:solid;border-width:1px;overflow:hidden;word-break:break-all;border-color:black;">'+arr[i].l_num+'</td>';
+                                                                        html+='<td class="tg-031e" style="font-family:Arial, sans-serif;font-size:18px;text-align:center;border-style:solid;border-width:1px;overflow:hidden;word-break:break-all;border-color:black;">'+arr[i].small_num+'件*'+parseInt(arr[i].num)+'张'+'</td>';
+                                                                    }else if (arr[i].mater_type==2){
+                                                                        html+= '<td class="tg-031e" style="font-family:Arial, sans-serif;font-size:18px;text-align:center;border-style:solid;border-width:1px;overflow:hidden;word-break:break-all;border-color:black;">kg</td>';
+                                                                        html+='<td class="tg-031e" style="font-family:Arial, sans-serif;font-size:18px;text-align:center;border-style:solid;border-width:1px;overflow:hidden;word-break:break-all;border-color:black;">'+arr[i].l_weight +'</td>';
+                                                                        html+='<td class="tg-031e" style="font-family:Arial, sans-serif;font-size:18px;text-align:center;border-style:solid;border-width:1px;overflow:hidden;word-break:break-all;border-color:black;">'+arr[i].small_num+'件*'+parseFloat(arr[i].num)/1000+'kg'+'</td>';
+                                                                    }else if (arr[i].mater_type==3){
+                                                                        html+= '<td class="tg-031e" style="font-family:Arial, sans-serif;font-size:18px;text-align:center;border-style:solid;border-width:1px;overflow:hidden;word-break:break-all;border-color:black;">万支</td>';
+                                                                        html+='<td class="tg-031e" style="font-family:Arial, sans-serif;font-size:18px;text-align:center;border-style:solid;border-width:1px;overflow:hidden;word-break:break-all;border-color:black;">'+parseFloat(arr[i].num)*parseFloat(arr[i].small_num)/10000+'</td>';
+                                                                        html+='<td class="tg-031e" style="font-family:Arial, sans-serif;font-size:18px;text-align:center;border-style:solid;border-width:1px;overflow:hidden;word-break:break-all;border-color:black;">'+arr[i].small_num+'件*'+parseInt(arr[i].num)+'支'+'</td>';
+                                                                    }
+                                                                }
+                                                                html+= '<tr><td class="tg-031e" style="font-family:Arial, sans-serif;font-size:18px;text-align:center;border-style:solid;border-width:1px;overflow:hidden;word-break:break-all;border-color:black;">送货单备注</td>' +
+                                                '                       <td colspan="6" class="tg-031e" style="font-family:Arial, sans-serif;font-size:18px;text-align:center;border-style:solid;border-width:1px;overflow:hidden;word-break:break-all;border-color:black;">'+res.data.note+'</td></tr>'+
+                                                '               </table>\n' +
+                                                '               <table class="tg2" style="margin-top: 3px;border-collapse:collapse;border-spacing:0;font-weight:500;width:1086px;font-size: 16px;">\n' +
+                                                '                   <tr class="footer">\n' +
+                                                '                       <td colspan="3">发货单位:<span class="company">'+res.data.supplier_name+'</span></td>\n' +
+                                                '                       <td colspan="3">司机/司机电话:<span id="deliveryman">'+res.data.deliveryman+'&nbsp;&nbsp;'+res.data.shr_phone+'</span></td>\n' +
+                                                '                       <td colspan="3">车牌号:<span id="carid">'+res.data.plate_number+'</span></td>\n' +
+                                                '                   </tr>\n' +
+                                                '                   <tr class="footer">\n' +
+                                                '                       <td colspan="7">发货单位地址:<span id="address">'+res.data.address+'</span></td>\n' +
+                                                '                       <td colspan="3">收货单位(签名、盖章)</td>\n' +
+                                                '                   </tr>\n' +
+                                                '               </table>'
+                                            $("#printcode").html(html)
+                                            var ee = $('#qrcode_image').attr('src',res.data.qrcode_add);
+                                            // 将打印的区域赋值,进行打印
+                                            ee.on('load',function () {
+                                                var printHTML = document.querySelector('#printcode').innerHTML;
+                                                window.document.body.innerHTML = printHTML;
+                                                window.print();
+                                                window.location.reload(); // 打印完成后重新加载页面
+                                            })
+                                        }
+                                        return false;
+                                    },
+                                    error: function (data, ret) {
+
+                                        return false;
+                                    }
+                                },
+
+                            ],
+                            formatter: Table.api.formatter.buttons
+                        },
+                        {field: 'status', title: '状态', operate: 'LIKE',searchList: {"0":'已发货',"1":'已删除'}, formatter: Table.api.formatter.status},
                         {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
                     ]
                 ],
             });
-
-
             // 为表格绑定事件
             Table.api.bindevent(table);
+            //去掉时间区间输入记忆
+            table.on('post-body.bs.table',function (e,settings,json,xhr) {
+                $('.datetimerange').each(function () {
+                    $(this).attr('autocomplete','off');
+                })
+            });
             Controller.api.bindevent();
         },
         api: {

+ 133 - 0
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ArrayEnabled.php

@@ -0,0 +1,133 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Calculation;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Engine\ArrayArgumentHelper;
+use PhpOffice\PhpSpreadsheet\Calculation\Engine\ArrayArgumentProcessor;
+
+trait ArrayEnabled
+{
+    /**
+     * @var ArrayArgumentHelper
+     */
+    private static $arrayArgumentHelper;
+
+    /**
+     * @param array|false $arguments Can be changed to array for Php8.1+
+     */
+    private static function initialiseHelper($arguments): void
+    {
+        if (self::$arrayArgumentHelper === null) {
+            self::$arrayArgumentHelper = new ArrayArgumentHelper();
+        }
+        self::$arrayArgumentHelper->initialise(($arguments === false) ? [] : $arguments);
+    }
+
+    /**
+     * Handles array argument processing when the function accepts a single argument that can be an array argument.
+     * Example use for:
+     *         DAYOFMONTH() or FACT().
+     */
+    protected static function evaluateSingleArgumentArray(callable $method, array $values): array
+    {
+        $result = [];
+        foreach ($values as $value) {
+            $result[] = $method($value);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Handles array argument processing when the function accepts multiple arguments,
+     *     and any of them can be an array argument.
+     * Example use for:
+     *         ROUND() or DATE().
+     *
+     * @param mixed ...$arguments
+     */
+    protected static function evaluateArrayArguments(callable $method, ...$arguments): array
+    {
+        self::initialiseHelper($arguments);
+        $arguments = self::$arrayArgumentHelper->arguments();
+
+        return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
+    }
+
+    /**
+     * Handles array argument processing when the function accepts multiple arguments,
+     *     but only the first few (up to limit) can be an array arguments.
+     * Example use for:
+     *         NETWORKDAYS() or CONCATENATE(), where the last argument is a matrix (or a series of values) that need
+     *                                         to be treated as a such rather than as an array arguments.
+     *
+     * @param mixed ...$arguments
+     */
+    protected static function evaluateArrayArgumentsSubset(callable $method, int $limit, ...$arguments): array
+    {
+        self::initialiseHelper(array_slice($arguments, 0, $limit));
+        $trailingArguments = array_slice($arguments, $limit);
+        $arguments = self::$arrayArgumentHelper->arguments();
+        $arguments = array_merge($arguments, $trailingArguments);
+
+        return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
+    }
+
+    /**
+     * @param mixed $value
+     */
+    private static function testFalse($value): bool
+    {
+        return $value === false;
+    }
+
+    /**
+     * Handles array argument processing when the function accepts multiple arguments,
+     *     but only the last few (from start) can be an array arguments.
+     * Example use for:
+     *         Z.TEST() or INDEX(), where the first argument 1 is a matrix that needs to be treated as a dataset
+     *                   rather than as an array argument.
+     *
+     * @param mixed ...$arguments
+     */
+    protected static function evaluateArrayArgumentsSubsetFrom(callable $method, int $start, ...$arguments): array
+    {
+        $arrayArgumentsSubset = array_combine(
+            range($start, count($arguments) - $start),
+            array_slice($arguments, $start)
+        );
+        if (self::testFalse($arrayArgumentsSubset)) {
+            return ['#VALUE!'];
+        }
+
+        self::initialiseHelper($arrayArgumentsSubset);
+        $leadingArguments = array_slice($arguments, 0, $start);
+        $arguments = self::$arrayArgumentHelper->arguments();
+        $arguments = array_merge($leadingArguments, $arguments);
+
+        return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
+    }
+
+    /**
+     * Handles array argument processing when the function accepts multiple arguments,
+     *     and any of them can be an array argument except for the one specified by ignore.
+     * Example use for:
+     *         HLOOKUP() and VLOOKUP(), where argument 1 is a matrix that needs to be treated as a database
+     *                                  rather than as an array argument.
+     *
+     * @param mixed ...$arguments
+     */
+    protected static function evaluateArrayArgumentsIgnore(callable $method, int $ignore, ...$arguments): array
+    {
+        $leadingArguments = array_slice($arguments, 0, $ignore);
+        $ignoreArgument = array_slice($arguments, $ignore, 1);
+        $trailingArguments = array_slice($arguments, $ignore + 1);
+
+        self::initialiseHelper(array_merge($leadingArguments, [[null]], $trailingArguments));
+        $arguments = self::$arrayArgumentHelper->arguments();
+
+        array_splice($arguments, $ignore, 1, $ignoreArgument);
+
+        return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
+    }
+}

+ 181 - 0
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/BinaryComparison.php

@@ -0,0 +1,181 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Calculation;
+
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+
+class BinaryComparison
+{
+    /**
+     * Epsilon Precision used for comparisons in calculations.
+     */
+    private const DELTA = 0.1e-12;
+
+    /**
+     * Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters.
+     *
+     * @param null|string $str1 First string value for the comparison
+     * @param null|string $str2 Second string value for the comparison
+     */
+    private static function strcmpLowercaseFirst($str1, $str2): int
+    {
+        $inversedStr1 = StringHelper::strCaseReverse($str1 ?? '');
+        $inversedStr2 = StringHelper::strCaseReverse($str2 ?? '');
+
+        return strcmp($inversedStr1, $inversedStr2);
+    }
+
+    /**
+     * PHP8.1 deprecates passing null to strcmp.
+     *
+     * @param null|string $str1 First string value for the comparison
+     * @param null|string $str2 Second string value for the comparison
+     */
+    private static function strcmpAllowNull($str1, $str2): int
+    {
+        return strcmp($str1 ?? '', $str2 ?? '');
+    }
+
+    /**
+     * @param mixed $operand1
+     * @param mixed $operand2
+     */
+    public static function compare($operand1, $operand2, string $operator): bool
+    {
+        //    Simple validate the two operands if they are string values
+        if (is_string($operand1) && $operand1 > '' && $operand1[0] == Calculation::FORMULA_STRING_QUOTE) {
+            $operand1 = Calculation::unwrapResult($operand1);
+        }
+        if (is_string($operand2) && $operand2 > '' && $operand2[0] == Calculation::FORMULA_STRING_QUOTE) {
+            $operand2 = Calculation::unwrapResult($operand2);
+        }
+
+        // Use case insensitive comparaison if not OpenOffice mode
+        if (Functions::getCompatibilityMode() != Functions::COMPATIBILITY_OPENOFFICE) {
+            if (is_string($operand1)) {
+                $operand1 = StringHelper::strToUpper($operand1);
+            }
+            if (is_string($operand2)) {
+                $operand2 = StringHelper::strToUpper($operand2);
+            }
+        }
+
+        $useLowercaseFirstComparison = is_string($operand1) &&
+            is_string($operand2) &&
+            Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE;
+
+        return self::evaluateComparison($operand1, $operand2, $operator, $useLowercaseFirstComparison);
+    }
+
+    /**
+     * @param mixed $operand1
+     * @param mixed $operand2
+     */
+    private static function evaluateComparison($operand1, $operand2, string $operator, bool $useLowercaseFirstComparison): bool
+    {
+        switch ($operator) {
+            //    Equality
+            case '=':
+                return self::equal($operand1, $operand2);
+            //    Greater than
+            case '>':
+                return self::greaterThan($operand1, $operand2, $useLowercaseFirstComparison);
+            //    Less than
+            case '<':
+                return self::lessThan($operand1, $operand2, $useLowercaseFirstComparison);
+            //    Greater than or equal
+            case '>=':
+                return self::greaterThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison);
+            //    Less than or equal
+            case '<=':
+                return self::lessThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison);
+            //    Inequality
+            case '<>':
+                return self::notEqual($operand1, $operand2);
+            default:
+                throw new Exception('Unsupported binary comparison operator');
+        }
+    }
+
+    /**
+     * @param mixed $operand1
+     * @param mixed $operand2
+     */
+    private static function equal($operand1, $operand2): bool
+    {
+        if (is_numeric($operand1) && is_numeric($operand2)) {
+            $result = (abs($operand1 - $operand2) < self::DELTA);
+        } elseif (($operand1 === null && is_numeric($operand2)) || ($operand2 === null && is_numeric($operand1))) {
+            $result = $operand1 == $operand2;
+        } else {
+            $result = self::strcmpAllowNull($operand1, $operand2) == 0;
+        }
+
+        return $result;
+    }
+
+    /**
+     * @param mixed $operand1
+     * @param mixed $operand2
+     */
+    private static function greaterThanOrEqual($operand1, $operand2, bool $useLowercaseFirstComparison): bool
+    {
+        if (is_numeric($operand1) && is_numeric($operand2)) {
+            $result = ((abs($operand1 - $operand2) < self::DELTA) || ($operand1 > $operand2));
+        } elseif (($operand1 === null && is_numeric($operand2)) || ($operand2 === null && is_numeric($operand1))) {
+            $result = $operand1 >= $operand2;
+        } elseif ($useLowercaseFirstComparison) {
+            $result = self::strcmpLowercaseFirst($operand1, $operand2) >= 0;
+        } else {
+            $result = self::strcmpAllowNull($operand1, $operand2) >= 0;
+        }
+
+        return $result;
+    }
+
+    /**
+     * @param mixed $operand1
+     * @param mixed $operand2
+     */
+    private static function lessThanOrEqual($operand1, $operand2, bool $useLowercaseFirstComparison): bool
+    {
+        if (is_numeric($operand1) && is_numeric($operand2)) {
+            $result = ((abs($operand1 - $operand2) < self::DELTA) || ($operand1 < $operand2));
+        } elseif (($operand1 === null && is_numeric($operand2)) || ($operand2 === null && is_numeric($operand1))) {
+            $result = $operand1 <= $operand2;
+        } elseif ($useLowercaseFirstComparison) {
+            $result = self::strcmpLowercaseFirst($operand1, $operand2) <= 0;
+        } else {
+            $result = self::strcmpAllowNull($operand1, $operand2) <= 0;
+        }
+
+        return $result;
+    }
+
+    /**
+     * @param mixed $operand1
+     * @param mixed $operand2
+     */
+    private static function greaterThan($operand1, $operand2, bool $useLowercaseFirstComparison): bool
+    {
+        return self::lessThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison) !== true;
+    }
+
+    /**
+     * @param mixed $operand1
+     * @param mixed $operand2
+     */
+    private static function lessThan($operand1, $operand2, bool $useLowercaseFirstComparison): bool
+    {
+        return self::greaterThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison) !== true;
+    }
+
+    /**
+     * @param mixed $operand1
+     * @param mixed $operand2
+     */
+    private static function notEqual($operand1, $operand2): bool
+    {
+        return self::equal($operand1, $operand2) !== true;
+    }
+}

Fichier diff supprimé car celui-ci est trop grand
+ 246 - 152
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php


+ 2 - 2
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DGet.php

@@ -2,7 +2,7 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\Database;
 
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class DGet extends DatabaseAbstract
 {
@@ -41,7 +41,7 @@ class DGet extends DatabaseAbstract
 
         $columnData = self::getFilteredColumn($database, $field, $criteria);
         if (count($columnData) > 1) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         $row = array_pop($columnData);

+ 19 - 19
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php

@@ -278,9 +278,9 @@ class DateTime
      *                                    or a standard date string
      * @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
      *                                    or a standard date string
-     * @param string $unit
+     * @param array|string $unit
      *
-     * @return int|string Interval between the dates
+     * @return array|int|string Interval between the dates
      */
     public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D')
     {
@@ -300,12 +300,12 @@ class DateTime
      * @See DateTimeExcel\Days::between()
      *      Use the between method in the DateTimeExcel\Days class instead
      *
-     * @param DateTimeInterface|float|int|string $endDate Excel date serial value (float),
+     * @param array|DateTimeInterface|float|int|string $endDate Excel date serial value (float),
      * PHP date timestamp (integer), PHP DateTime object, or a standard date string
-     * @param DateTimeInterface|float|int|string $startDate Excel date serial value (float),
+     * @param array|DateTimeInterface|float|int|string $startDate Excel date serial value (float),
      * PHP date timestamp (integer), PHP DateTime object, or a standard date string
      *
-     * @return int|string Number of days between start date and end date or an error
+     * @return array|int|string Number of days between start date and end date or an error
      */
     public static function DAYS($endDate = 0, $startDate = 0)
     {
@@ -331,7 +331,7 @@ class DateTime
      *                                        PHP DateTime object, or a standard date string
      * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
      *                                        PHP DateTime object, or a standard date string
-     * @param bool $method US or European Method
+     * @param array|bool $method US or European Method
      *                                        FALSE or omitted: U.S. (NASD) method. If the starting date is
      *                                        the last day of a month, it becomes equal to the 30th of the
      *                                        same month. If the ending date is the last day of a month and
@@ -343,7 +343,7 @@ class DateTime
      *                                        occur on the 31st of a month become equal to the 30th of the
      *                                        same month.
      *
-     * @return int|string Number of days between start date and end date
+     * @return array|int|string Number of days between start date and end date
      */
     public static function DAYS360($startDate = 0, $endDate = 0, $method = false)
     {
@@ -373,14 +373,14 @@ class DateTime
      *                                    PHP DateTime object, or a standard date string
      * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard date string
-     * @param int $method Method used for the calculation
+     * @param array|int $method Method used for the calculation
      *                                        0 or omitted    US (NASD) 30/360
      *                                        1                Actual/actual
      *                                        2                Actual/360
      *                                        3                Actual/365
      *                                        4                European 30/360
      *
-     * @return float|string fraction of the year, or a string containing an error
+     * @return array|float|string fraction of the year, or a string containing an error
      */
     public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
     {
@@ -409,7 +409,7 @@ class DateTime
      *                                            PHP DateTime object, or a standard date string
      * @param mixed $dateArgs
      *
-     * @return int|string Interval between the dates
+     * @return array|int|string Interval between the dates
      */
     public static function NETWORKDAYS($startDate, $endDate, ...$dateArgs)
     {
@@ -464,7 +464,7 @@ class DateTime
      * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard date string
      *
-     * @return int|string Day of the month
+     * @return array|int|string Day of the month
      */
     public static function DAYOFMONTH($dateValue = 1)
     {
@@ -492,7 +492,7 @@ class DateTime
      *                                        2                Numbers 1 (Monday) through 7 (Sunday).
      *                                        3                Numbers 0 (Monday) through 6 (Sunday).
      *
-     * @return int|string Day of the week value
+     * @return array|int|string Day of the week value
      */
     public static function WEEKDAY($dateValue = 1, $style = 1)
     {
@@ -704,7 +704,7 @@ class DateTime
      *                                        17               Week begins on Sunday.
      *                                        21               ISO (Jan. 4 is week 1, begins on Monday).
      *
-     * @return int|string Week Number
+     * @return array|int|string Week Number
      */
     public static function WEEKNUM($dateValue = 1, $method = self::STARTWEEK_SUNDAY)
     {
@@ -727,7 +727,7 @@ class DateTime
      * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard date string
      *
-     * @return int|string Week Number
+     * @return array|int|string Week Number
      */
     public static function ISOWEEKNUM($dateValue = 1)
     {
@@ -751,7 +751,7 @@ class DateTime
      * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard date string
      *
-     * @return int|string Month of the year
+     * @return array|int|string Month of the year
      */
     public static function MONTHOFYEAR($dateValue = 1)
     {
@@ -775,7 +775,7 @@ class DateTime
      * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard date string
      *
-     * @return int|string Year
+     * @return array|int|string Year
      */
     public static function YEAR($dateValue = 1)
     {
@@ -799,7 +799,7 @@ class DateTime
      * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard time string
      *
-     * @return int|string Hour
+     * @return array|int|string Hour
      */
     public static function HOUROFDAY($timeValue = 0)
     {
@@ -823,7 +823,7 @@ class DateTime
      * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard time string
      *
-     * @return int|string Minute
+     * @return array|int|string Minute
      */
     public static function MINUTE($timeValue = 0)
     {
@@ -847,7 +847,7 @@ class DateTime
      * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard time string
      *
-     * @return int|string Second
+     * @return array|int|string Second
      */
     public static function SECOND($timeValue = 0)
     {

+ 5 - 5
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php

@@ -3,7 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
 use DateTimeImmutable;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Current
 {
@@ -27,9 +27,9 @@ class Current
     public static function today()
     {
         $dti = new DateTimeImmutable();
-        $dateArray = date_parse($dti->format('c'));
+        $dateArray = Helpers::dateParse($dti->format('c'));
 
-        return is_array($dateArray) ? Helpers::returnIn3FormatsArray($dateArray, true) : Functions::VALUE();
+        return Helpers::dateParseSucceeded($dateArray) ? Helpers::returnIn3FormatsArray($dateArray, true) : ExcelError::VALUE();
     }
 
     /**
@@ -52,8 +52,8 @@ class Current
     public static function now()
     {
         $dti = new DateTimeImmutable();
-        $dateArray = date_parse($dti->format('c'));
+        $dateArray = Helpers::dateParse($dti->format('c'));
 
-        return is_array($dateArray) ? Helpers::returnIn3FormatsArray($dateArray) : Functions::VALUE();
+        return Helpers::dateParseSucceeded($dateArray) ? Helpers::returnIn3FormatsArray($dateArray) : ExcelError::VALUE();
     }
 }

+ 19 - 15
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php

@@ -2,13 +2,16 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 
 class Date
 {
+    use ArrayEnabled;
+
     /**
      * DATE.
      *
@@ -24,7 +27,7 @@ class Date
      * A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted,
      *     as will a day value with a suffix (e.g. '21st' rather than simply 21); again only English language.
      *
-     * @param int $year The value of the year argument can include one to four digits.
+     * @param array|int $year The value of the year argument can include one to four digits.
      *                                Excel interprets the year argument according to the configured
      *                                date system: 1900 or 1904.
      *                                If year is between 0 (zero) and 1899 (inclusive), Excel adds that
@@ -35,7 +38,7 @@ class Date
      *                                2008.
      *                                If year is less than 0 or is 10000 or greater, Excel returns the
      *                                #NUM! error value.
-     * @param int $month A positive or negative integer representing the month of the year
+     * @param array|int $month A positive or negative integer representing the month of the year
      *                                from 1 to 12 (January to December).
      *                                If month is greater than 12, month adds that number of months to
      *                                the first month in the year specified. For example, DATE(2008,14,2)
@@ -44,7 +47,7 @@ class Date
      *                                number of months, plus 1, from the first month in the year
      *                                specified. For example, DATE(2008,-3,2) returns the serial number
      *                                representing September 2, 2007.
-     * @param int $day A positive or negative integer representing the day of the month
+     * @param array|int $day A positive or negative integer representing the day of the month
      *                                from 1 to 31.
      *                                If day is greater than the number of days in the month specified,
      *                                day adds that number of days to the first day in the month. For
@@ -57,9 +60,15 @@ class Date
      *
      * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
      *                        depending on the value of the ReturnDateType flag
+     *         If an array of numbers is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function fromYMD($year, $month, $day)
     {
+        if (is_array($year) || is_array($month) || is_array($day)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $year, $month, $day);
+        }
+
         $baseYear = SharedDateHelper::getExcelCalendar();
 
         try {
@@ -84,18 +93,17 @@ class Date
      */
     private static function getYear($year, int $baseYear): int
     {
-        $year = Functions::flattenSingleValue($year);
         $year = ($year !== null) ? StringHelper::testStringAsNumeric((string) $year) : 0;
         if (!is_numeric($year)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
         $year = (int) $year;
 
         if ($year < ($baseYear - 1900)) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
         if ((($baseYear - 1900) !== 0) && ($year < $baseYear) && ($year >= 1900)) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         if (($year < $baseYear) && ($year >= ($baseYear - 1900))) {
@@ -112,15 +120,13 @@ class Date
      */
     private static function getMonth($month): int
     {
-        $month = Functions::flattenSingleValue($month);
-
         if (($month !== null) && (!is_numeric($month))) {
             $month = SharedDateHelper::monthStringToNumber($month);
         }
 
         $month = ($month !== null) ? StringHelper::testStringAsNumeric((string) $month) : 0;
         if (!is_numeric($month)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
 
         return (int) $month;
@@ -133,15 +139,13 @@ class Date
      */
     private static function getDay($day): int
     {
-        $day = Functions::flattenSingleValue($day);
-
         if (($day !== null) && (!is_numeric($day))) {
             $day = SharedDateHelper::dayStringToNumber($day);
         }
 
         $day = ($day !== null) ? StringHelper::testStringAsNumeric((string) $day) : 0;
         if (!is_numeric($day)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
 
         return (int) $day;
@@ -162,7 +166,7 @@ class Date
 
         // Re-validate the year parameter after adjustments
         if (($year < $baseYear) || ($year >= 10000)) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
     }
 }

+ 27 - 3
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php

@@ -2,12 +2,15 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
 
 class DateParts
 {
+    use ArrayEnabled;
+
     /**
      * DAYOFMONTH.
      *
@@ -19,11 +22,18 @@ class DateParts
      *
      * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard date string
+     *                         Or can be an array of date values
      *
-     * @return int|string Day of the month
+     * @return array|int|string Day of the month
+     *         If an array of numbers is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function day($dateValue)
     {
+        if (is_array($dateValue)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
+        }
+
         $weirdResult = self::weirdCondition($dateValue);
         if ($weirdResult >= 0) {
             return $weirdResult;
@@ -52,11 +62,18 @@ class DateParts
      *
      * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard date string
+     *                         Or can be an array of date values
      *
-     * @return int|string Month of the year
+     * @return array|int|string Month of the year
+     *         If an array of numbers is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function month($dateValue)
     {
+        if (is_array($dateValue)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
+        }
+
         try {
             $dateValue = Helpers::getDateValue($dateValue);
         } catch (Exception $e) {
@@ -83,11 +100,18 @@ class DateParts
      *
      * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard date string
+     *                         Or can be an array of date values
      *
-     * @return int|string Year
+     * @return array|int|string Year
+     *         If an array of numbers is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function year($dateValue)
     {
+        if (is_array($dateValue)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
+        }
+
         try {
             $dateValue = Helpers::getDateValue($dateValue);
         } catch (Exception $e) {

+ 27 - 21
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php

@@ -3,11 +3,14 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
 use DateTimeImmutable;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
 
 class DateValue
 {
+    use ArrayEnabled;
+
     /**
      * DATEVALUE.
      *
@@ -21,7 +24,7 @@ class DateValue
      * Excel Function:
      *        DATEVALUE(dateValue)
      *
-     * @param string $dateValue Text that represents a date in a Microsoft Excel date format.
+     * @param array|string $dateValue Text that represents a date in a Microsoft Excel date format.
      *                                    For example, "1/30/2008" or "30-Jan-2008" are text strings within
      *                                    quotation marks that represent dates. Using the default date
      *                                    system in Excel for Windows, date_text must represent a date from
@@ -29,17 +32,24 @@ class DateValue
      *                                    system in Excel for the Macintosh, date_text must represent a date
      *                                    from January 1, 1904, to December 31, 9999. DATEVALUE returns the
      *                                    #VALUE! error value if date_text is out of this range.
+     *                         Or can be an array of date values
      *
      * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
      *                        depending on the value of the ReturnDateType flag
+     *         If an array of numbers is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function fromString($dateValue)
     {
+        if (is_array($dateValue)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
+        }
+
         $dti = new DateTimeImmutable();
         $baseYear = SharedDateHelper::getExcelCalendar();
-        $dateValue = trim(Functions::flattenSingleValue($dateValue ?? ''), '"');
+        $dateValue = trim($dateValue ?? '', '"');
         //    Strip any ordinals because they're allowed in Excel (English only)
-        $dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue) ?? '';
+        $dateValue = (string) preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue);
         //    Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany)
         $dateValue = str_replace(['/', '.', '-', '  '], ' ', $dateValue);
 
@@ -49,7 +59,7 @@ class DateValue
         foreach ($t1 as &$t) {
             if ((is_numeric($t)) && ($t > 31)) {
                 if ($yearFound) {
-                    return Functions::VALUE();
+                    return ExcelError::VALUE();
                 }
                 if ($t < 100) {
                     $t += 1900;
@@ -59,7 +69,7 @@ class DateValue
         }
         if (count($t1) === 1) {
             //    We've been fed a time value without any date
-            return ((strpos((string) $t, ':') === false)) ? Functions::Value() : 0.0;
+            return ((strpos((string) $t, ':') === false)) ? ExcelError::Value() : 0.0;
         }
         unset($t);
 
@@ -92,13 +102,11 @@ class DateValue
 
     /**
      * Parse date.
-     *
-     * @return array|bool
      */
-    private static function setUpArray(string $dateValue, DateTimeImmutable $dti)
+    private static function setUpArray(string $dateValue, DateTimeImmutable $dti): array
     {
-        $PHPDateArray = date_parse($dateValue);
-        if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
+        $PHPDateArray = Helpers::dateParse($dateValue);
+        if (!Helpers::dateParseSucceeded($PHPDateArray)) {
             // If original count was 1, we've already returned.
             // If it was 2, we added another.
             // Therefore, neither of the first 2 stroks below can fail.
@@ -106,9 +114,9 @@ class DateValue
             $testVal2 = strtok('- ');
             $testVal3 = strtok('- ') ?: $dti->format('Y');
             Helpers::adjustYear((string) $testVal1, (string) $testVal2, $testVal3);
-            $PHPDateArray = date_parse($testVal1 . '-' . $testVal2 . '-' . $testVal3);
-            if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
-                $PHPDateArray = date_parse($testVal2 . '-' . $testVal1 . '-' . $testVal3);
+            $PHPDateArray = Helpers::dateParse($testVal1 . '-' . $testVal2 . '-' . $testVal3);
+            if (!Helpers::dateParseSucceeded($PHPDateArray)) {
+                $PHPDateArray = Helpers::dateParse($testVal2 . '-' . $testVal1 . '-' . $testVal3);
             }
         }
 
@@ -118,19 +126,17 @@ class DateValue
     /**
      * Final results.
      *
-     * @param array|bool $PHPDateArray
-     *
      * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
      *                        depending on the value of the ReturnDateType flag
      */
-    private static function finalResults($PHPDateArray, DateTimeImmutable $dti, int $baseYear)
+    private static function finalResults(array $PHPDateArray, DateTimeImmutable $dti, int $baseYear)
     {
-        $retValue = Functions::Value();
-        if (is_array($PHPDateArray) && $PHPDateArray['error_count'] == 0) {
+        $retValue = ExcelError::Value();
+        if (Helpers::dateParseSucceeded($PHPDateArray)) {
             // Execute function
             Helpers::replaceIfEmpty($PHPDateArray['year'], $dti->format('Y'));
             if ($PHPDateArray['year'] < $baseYear) {
-                return Functions::VALUE();
+                return ExcelError::VALUE();
             }
             Helpers::replaceIfEmpty($PHPDateArray['month'], $dti->format('m'));
             Helpers::replaceIfEmpty($PHPDateArray['day'], $dti->format('d'));
@@ -141,7 +147,7 @@ class DateValue
             $day = (int) $PHPDateArray['day'];
             $year = (int) $PHPDateArray['year'];
             if (!checkdate($month, $day, $year)) {
-                return ($year === 1900 && $month === 2 && $day === 29) ? Helpers::returnIn3FormatsFloat(60.0) : Functions::VALUE();
+                return ($year === 1900 && $month === 2 && $day === 29) ? Helpers::returnIn3FormatsFloat(60.0) : ExcelError::VALUE();
             }
             $retValue = Helpers::returnIn3FormatsArray($PHPDateArray, true);
         }

+ 18 - 7
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php

@@ -3,12 +3,15 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
 use DateTimeInterface;
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
 
 class Days
 {
+    use ArrayEnabled;
+
     /**
      * DAYS.
      *
@@ -17,15 +20,23 @@ class Days
      * Excel Function:
      *        DAYS(endDate, startDate)
      *
-     * @param DateTimeInterface|float|int|string $endDate Excel date serial value (float),
-     * PHP date timestamp (integer), PHP DateTime object, or a standard date string
-     * @param DateTimeInterface|float|int|string $startDate Excel date serial value (float),
-     * PHP date timestamp (integer), PHP DateTime object, or a standard date string
+     * @param array|DateTimeInterface|float|int|string $endDate Excel date serial value (float),
+     *           PHP date timestamp (integer), PHP DateTime object, or a standard date string
+     *                         Or can be an array of date values
+     * @param array|DateTimeInterface|float|int|string $startDate Excel date serial value (float),
+     *           PHP date timestamp (integer), PHP DateTime object, or a standard date string
+     *                         Or can be an array of date values
      *
-     * @return int|string Number of days between start date and end date or an error
+     * @return array|int|string Number of days between start date and end date or an error
+     *         If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
+     *            will also be an array with matching dimensions
      */
     public static function between($endDate, $startDate)
     {
+        if (is_array($endDate) || is_array($startDate)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $endDate, $startDate);
+        }
+
         try {
             $startDate = Helpers::getDateValue($startDate);
             $endDate = Helpers::getDateValue($endDate);
@@ -37,7 +48,7 @@ class Days
         $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
         $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
 
-        $days = Functions::VALUE();
+        $days = ExcelError::VALUE();
         $diff = $PHPStartDateObject->diff($PHPEndDateObject);
         if ($diff !== false && !is_bool($diff->days)) {
             $days = $diff->days;

+ 18 - 6
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php

@@ -2,12 +2,15 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
 
 class Days360
 {
+    use ArrayEnabled;
+
     /**
      * DAYS360.
      *
@@ -18,11 +21,13 @@ class Days360
      * Excel Function:
      *        DAYS360(startDate,endDate[,method])
      *
-     * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
+     * @param array|mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
      *                                        PHP DateTime object, or a standard date string
-     * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
+     *                         Or can be an array of date values
+     * @param array|mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
      *                                        PHP DateTime object, or a standard date string
-     * @param mixed $method US or European Method as a bool
+     *                         Or can be an array of date values
+     * @param array|mixed $method US or European Method as a bool
      *                                        FALSE or omitted: U.S. (NASD) method. If the starting date is
      *                                        the last day of a month, it becomes equal to the 30th of the
      *                                        same month. If the ending date is the last day of a month and
@@ -33,11 +38,18 @@ class Days360
      *                                        TRUE: European method. Starting dates and ending dates that
      *                                        occur on the 31st of a month become equal to the 30th of the
      *                                        same month.
+     *                         Or can be an array of methods
      *
-     * @return int|string Number of days between start date and end date
+     * @return array|int|string Number of days between start date and end date
+     *         If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
+     *            will also be an array with matching dimensions
      */
     public static function between($startDate = 0, $endDate = 0, $method = false)
     {
+        if (is_array($startDate) || is_array($endDate) || is_array($method)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $startDate, $endDate, $method);
+        }
+
         try {
             $startDate = Helpers::getDateValue($startDate);
             $endDate = Helpers::getDateValue($endDate);
@@ -46,7 +58,7 @@ class Days360
         }
 
         if (!is_bool($method)) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
 
         // Execute function

+ 18 - 6
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php

@@ -4,30 +4,42 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
 use DateInterval;
 use DateTime;
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
 
 class Difference
 {
+    use ArrayEnabled;
+
     /**
      * DATEDIF.
      *
      * @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
      *                                    or a standard date string
+     *                         Or can be an array of date values
      * @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
      *                                    or a standard date string
-     * @param string $unit
+     *                         Or can be an array of date values
+     * @param array|string $unit
+     *                         Or can be an array of unit values
      *
-     * @return int|string Interval between the dates
+     * @return array|int|string Interval between the dates
+     *         If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
+     *            will also be an array with matching dimensions
      */
     public static function interval($startDate, $endDate, $unit = 'D')
     {
+        if (is_array($startDate) || is_array($endDate) || is_array($unit)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $startDate, $endDate, $unit);
+        }
+
         try {
             $startDate = Helpers::getDateValue($startDate);
             $endDate = Helpers::getDateValue($endDate);
             $difference = self::initialDiff($startDate, $endDate);
-            $unit = strtoupper(Functions::flattenSingleValue($unit));
+            $unit = strtoupper($unit);
         } catch (Exception $e) {
             return $e->getMessage();
         }
@@ -53,14 +65,14 @@ class Difference
         $retVal = self::replaceRetValue($retVal, $unit, 'YD') ?? self::datedifYD($difference, $startYears, $endYears, $PHPStartDateObject, $PHPEndDateObject);
         $retVal = self::replaceRetValue($retVal, $unit, 'YM') ?? self::datedifYM($PHPDiffDateObject);
 
-        return is_bool($retVal) ? Functions::VALUE() : $retVal;
+        return is_bool($retVal) ? ExcelError::VALUE() : $retVal;
     }
 
     private static function initialDiff(float $startDate, float $endDate): float
     {
         // Validate parameters
         if ($startDate > $endDate) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $endDate - $startDate;

+ 28 - 6
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php

@@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 use DateTime;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
 
 class Helpers
@@ -33,7 +34,7 @@ class Helpers
         if (is_object($dateValue)) {
             $retval = SharedDateHelper::PHPToExcel($dateValue);
             if (is_bool($retval)) {
-                throw new Exception(Functions::VALUE());
+                throw new Exception(ExcelError::VALUE());
             }
 
             return $retval;
@@ -46,11 +47,11 @@ class Helpers
             $dateValue = DateValue::fromString($dateValue);
             Functions::setReturnDateType($saveReturnDateType);
             if (!is_numeric($dateValue)) {
-                throw new Exception(Functions::VALUE());
+                throw new Exception(ExcelError::VALUE());
             }
         }
         if ($dateValue < 0 && Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return (float) $dateValue;
@@ -253,7 +254,7 @@ class Helpers
             return (float) $number;
         }
 
-        throw new Exception(Functions::VALUE());
+        throw new Exception(ExcelError::VALUE());
     }
 
     /**
@@ -266,13 +267,13 @@ class Helpers
     public static function validateNotNegative($number)
     {
         if (!is_numeric($number)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
         if ($number >= 0) {
             return (float) $number;
         }
 
-        throw new Exception(Functions::NAN());
+        throw new Exception(ExcelError::NAN());
     }
 
     public static function silly1900(DateTime $PHPDateObject, string $mod = '-1 day'): void
@@ -282,4 +283,25 @@ class Helpers
             $PHPDateObject->modify($mod);
         }
     }
+
+    public static function dateParse(string $string): array
+    {
+        return self::forceArray(date_parse($string));
+    }
+
+    public static function dateParseSucceeded(array $dateArray): bool
+    {
+        return $dateArray['error_count'] === 0;
+    }
+
+    /**
+     * Despite documentation, date_parse probably never returns false.
+     * Just in case, this routine helps guarantee it.
+     *
+     * @param array|false $dateArray
+     */
+    private static function forceArray($dateArray): array
+    {
+        return is_array($dateArray) ? $dateArray : ['error_count' => 1];
+    }
 }

+ 23 - 4
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php

@@ -2,10 +2,13 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 
 class Month
 {
+    use ArrayEnabled;
+
     /**
      * EDATE.
      *
@@ -19,15 +22,23 @@ class Month
      *
      * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
      *                                        PHP DateTime object, or a standard date string
-     * @param int $adjustmentMonths The number of months before or after start_date.
+     *                         Or can be an array of date values
+     * @param array|int $adjustmentMonths The number of months before or after start_date.
      *                                        A positive value for months yields a future date;
      *                                        a negative value yields a past date.
+     *                         Or can be an array of adjustment values
      *
-     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+     * @return array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
      *                        depending on the value of the ReturnDateType flag
+     *         If an array of values is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function adjust($dateValue, $adjustmentMonths)
     {
+        if (is_array($dateValue) || is_array($adjustmentMonths)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $adjustmentMonths);
+        }
+
         try {
             $dateValue = Helpers::getDateValue($dateValue, false);
             $adjustmentMonths = Helpers::validateNumericNull($adjustmentMonths);
@@ -54,15 +65,23 @@ class Month
      *
      * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
      *                                        PHP DateTime object, or a standard date string
-     * @param int $adjustmentMonths The number of months before or after start_date.
+     *                         Or can be an array of date values
+     * @param array|int $adjustmentMonths The number of months before or after start_date.
      *                                        A positive value for months yields a future date;
      *                                        a negative value yields a past date.
+     *                         Or can be an array of adjustment values
      *
-     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+     * @return array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
      *                        depending on the value of the ReturnDateType flag
+     *         If an array of values is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function lastDay($dateValue, $adjustmentMonths)
     {
+        if (is_array($dateValue) || is_array($adjustmentMonths)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $adjustmentMonths);
+        }
+
         try {
             $dateValue = Helpers::getDateValue($dateValue, false);
             $adjustmentMonths = Helpers::validateNumericNull($adjustmentMonths);

+ 19 - 2
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php

@@ -2,11 +2,14 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 
 class NetworkDays
 {
+    use ArrayEnabled;
+
     /**
      * NETWORKDAYS.
      *
@@ -20,14 +23,28 @@ class NetworkDays
      *
      * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
      *                                            PHP DateTime object, or a standard date string
+     *                         Or can be an array of date values
      * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
      *                                            PHP DateTime object, or a standard date string
-     * @param mixed $dateArgs
+     *                         Or can be an array of date values
+     * @param mixed $dateArgs An array of dates (such as holidays) to exclude from the calculation
      *
-     * @return int|string Interval between the dates
+     * @return array|int|string Interval between the dates
+     *         If an array of values is passed for the $startDate or $endDate arguments, then the returned result
+     *            will also be an array with matching dimensions
      */
     public static function count($startDate, $endDate, ...$dateArgs)
     {
+        if (is_array($startDate) || is_array($endDate)) {
+            return self::evaluateArrayArgumentsSubset(
+                [self::class, __FUNCTION__],
+                2,
+                $startDate,
+                $endDate,
+                ...$dateArgs
+            );
+        }
+
         try {
             //    Retrieve the mandatory start and end date that are referenced in the function definition
             $sDate = Helpers::getDateValue($startDate);

+ 18 - 7
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php

@@ -3,12 +3,16 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
 use DateTime;
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
 
 class Time
 {
+    use ArrayEnabled;
+
     /**
      * TIME.
      *
@@ -20,23 +24,31 @@ class Time
      * Excel Function:
      *        TIME(hour,minute,second)
      *
-     * @param mixed $hour A number from 0 (zero) to 32767 representing the hour.
+     * @param array|int $hour A number from 0 (zero) to 32767 representing the hour.
      *                                    Any value greater than 23 will be divided by 24 and the remainder
      *                                    will be treated as the hour value. For example, TIME(27,0,0) =
      *                                    TIME(3,0,0) = .125 or 3:00 AM.
-     * @param mixed $minute A number from 0 to 32767 representing the minute.
+     * @param array|int $minute A number from 0 to 32767 representing the minute.
      *                                    Any value greater than 59 will be converted to hours and minutes.
      *                                    For example, TIME(0,750,0) = TIME(12,30,0) = .520833 or 12:30 PM.
-     * @param mixed $second A number from 0 to 32767 representing the second.
+     * @param array|int $second A number from 0 to 32767 representing the second.
      *                                    Any value greater than 59 will be converted to hours, minutes,
      *                                    and seconds. For example, TIME(0,0,2000) = TIME(0,33,22) = .023148
      *                                    or 12:33:20 AM
+     *         If an array of numbers is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      *
-     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+     * @return array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
      *                        depending on the value of the ReturnDateType flag
+     *         If an array of numbers is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function fromHMS($hour, $minute, $second)
     {
+        if (is_array($hour) || is_array($minute) || is_array($second)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $hour, $minute, $second);
+        }
+
         try {
             $hour = self::toIntWithNullBool($hour);
             $minute = self::toIntWithNullBool($minute);
@@ -51,7 +63,7 @@ class Time
         if ($hour > 23) {
             $hour = $hour % 24;
         } elseif ($hour < 0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         // Execute function
@@ -105,13 +117,12 @@ class Time
      */
     private static function toIntWithNullBool($value): int
     {
-        $value = Functions::flattenSingleValue($value);
         $value = $value ?? 0;
         if (is_bool($value)) {
             $value = (int) $value;
         }
         if (!is_numeric($value)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
 
         return (int) $value;

+ 27 - 7
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php

@@ -2,12 +2,14 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
 
 class TimeParts
 {
+    use ArrayEnabled;
+
     /**
      * HOUROFDAY.
      *
@@ -19,13 +21,19 @@ class TimeParts
      *
      * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard time string
+     *                         Or can be an array of date/time values
      *
-     * @return int|string Hour
+     * @return array|int|string Hour
+     *         If an array of numbers is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function hour($timeValue)
     {
+        if (is_array($timeValue)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
+        }
+
         try {
-            $timeValue = Functions::flattenSingleValue($timeValue);
             Helpers::nullFalseTrueToNumber($timeValue);
             if (!is_numeric($timeValue)) {
                 $timeValue = Helpers::getTimeValue($timeValue);
@@ -53,13 +61,19 @@ class TimeParts
      *
      * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard time string
+     *                         Or can be an array of date/time values
      *
-     * @return int|string Minute
+     * @return array|int|string Minute
+     *         If an array of numbers is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function minute($timeValue)
     {
+        if (is_array($timeValue)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
+        }
+
         try {
-            $timeValue = Functions::flattenSingleValue($timeValue);
             Helpers::nullFalseTrueToNumber($timeValue);
             if (!is_numeric($timeValue)) {
                 $timeValue = Helpers::getTimeValue($timeValue);
@@ -87,13 +101,19 @@ class TimeParts
      *
      * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard time string
+     *                         Or can be an array of date/time values
      *
-     * @return int|string Second
+     * @return array|int|string Second
+     *         If an array of numbers is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function second($timeValue)
     {
+        if (is_array($timeValue)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
+        }
+
         try {
-            $timeValue = Functions::flattenSingleValue($timeValue);
             Helpers::nullFalseTrueToNumber($timeValue);
             if (!is_numeric($timeValue)) {
                 $timeValue = Helpers::getTimeValue($timeValue);

+ 23 - 6
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php

@@ -3,11 +3,15 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
 use Datetime;
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
 
 class TimeValue
 {
+    use ArrayEnabled;
+
     /**
      * TIMEVALUE.
      *
@@ -21,17 +25,24 @@ class TimeValue
      * Excel Function:
      *        TIMEVALUE(timeValue)
      *
-     * @param string $timeValue A text string that represents a time in any one of the Microsoft
+     * @param array|string $timeValue A text string that represents a time in any one of the Microsoft
      *                                    Excel time formats; for example, "6:45 PM" and "18:45" text strings
      *                                    within quotation marks that represent time.
      *                                    Date information in time_text is ignored.
+     *                         Or can be an array of date/time values
      *
      * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
      *                        depending on the value of the ReturnDateType flag
+     *         If an array of numbers is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function fromString($timeValue)
     {
-        $timeValue = trim(Functions::flattenSingleValue($timeValue ?? ''), '"');
+        if (is_array($timeValue)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
+        }
+
+        $timeValue = trim($timeValue ?? '', '"');
         $timeValue = str_replace(['/', '.'], '-', $timeValue);
 
         $arraySplit = preg_split('/[\/:\-\s]/', $timeValue) ?: [];
@@ -40,11 +51,17 @@ class TimeValue
             $timeValue = implode(':', $arraySplit);
         }
 
-        $PHPDateArray = date_parse($timeValue);
-        $retValue = Functions::VALUE();
-        if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
+        $PHPDateArray = Helpers::dateParse($timeValue);
+        $retValue = ExcelError::VALUE();
+        if (Helpers::dateParseSucceeded($PHPDateArray)) {
+            /** @var int */
+            $hour = $PHPDateArray['hour'];
+            /** @var int */
+            $minute = $PHPDateArray['minute'];
+            /** @var int */
+            $second = $PHPDateArray['second'];
             // OpenOffice-specific code removed - it works just like Excel
-            $excelDateValue = SharedDateHelper::formattedPHPToExcel(1900, 1, 1, $PHPDateArray['hour'], $PHPDateArray['minute'], $PHPDateArray['second']) - 1;
+            $excelDateValue = SharedDateHelper::formattedPHPToExcel(1900, 1, 1, $hour, $minute, $second) - 1;
 
             $retType = Functions::getReturnDateType();
             if ($retType === Functions::RETURNDATE_EXCEL) {

+ 38 - 14
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php

@@ -3,12 +3,15 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
 use DateTime;
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
 
 class Week
 {
+    use ArrayEnabled;
+
     /**
      * WEEKNUM.
      *
@@ -24,7 +27,8 @@ class Week
      *
      * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard date string
-     * @param int $method Week begins on Sunday or Monday
+     *                         Or can be an array of date values
+     * @param array|int $method Week begins on Sunday or Monday
      *                                        1 or omitted    Week begins on Sunday.
      *                                        2                Week begins on Monday.
      *                                        11               Week begins on Monday.
@@ -35,11 +39,18 @@ class Week
      *                                        16               Week begins on Saturday.
      *                                        17               Week begins on Sunday.
      *                                        21               ISO (Jan. 4 is week 1, begins on Monday).
+     *                         Or can be an array of methods
      *
-     * @return int|string Week Number
+     * @return array|int|string Week Number
+     *         If an array of values is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function number($dateValue, $method = Constants::STARTWEEK_SUNDAY)
     {
+        if (is_array($dateValue) || is_array($method)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $method);
+        }
+
         $origDateValueNull = empty($dateValue);
 
         try {
@@ -88,11 +99,18 @@ class Week
      *
      * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard date string
+     *                         Or can be an array of date values
      *
-     * @return int|string Week Number
+     * @return array|int|string Week Number
+     *         If an array of numbers is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function isoWeekNumber($dateValue)
     {
+        if (is_array($dateValue)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
+        }
+
         if (self::apparentBug($dateValue)) {
             return 52;
         }
@@ -119,17 +137,25 @@ class Week
      * Excel Function:
      *        WEEKDAY(dateValue[,style])
      *
-     * @param null|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
+     * @param null|array|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard date string
+     *                         Or can be an array of date values
      * @param mixed $style A number that determines the type of return value
      *                                        1 or omitted    Numbers 1 (Sunday) through 7 (Saturday).
      *                                        2                Numbers 1 (Monday) through 7 (Sunday).
      *                                        3                Numbers 0 (Monday) through 6 (Sunday).
+     *                         Or can be an array of styles
      *
-     * @return int|string Day of the week value
+     * @return array|int|string Day of the week value
+     *         If an array of values is passed as the argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function day($dateValue, $style = 1)
     {
+        if (is_array($dateValue) || is_array($style)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $style);
+        }
+
         try {
             $dateValue = Helpers::getDateValue($dateValue);
             $style = self::validateStyle($style);
@@ -165,14 +191,12 @@ class Week
      */
     private static function validateStyle($style): int
     {
-        $style = Functions::flattenSingleValue($style);
-
         if (!is_numeric($style)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
         $style = (int) $style;
         if (($style < 1) || ($style > 3)) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $style;
@@ -209,7 +233,7 @@ class Week
     private static function validateDateValue($dateValue): float
     {
         if (is_bool($dateValue)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
 
         return Helpers::getDateValue($dateValue);
@@ -225,14 +249,14 @@ class Week
         if ($method === null) {
             $method = Constants::STARTWEEK_SUNDAY;
         }
-        $method = Functions::flattenSingleValue($method);
+
         if (!is_numeric($method)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
 
         $method = (int) $method;
         if (!array_key_exists($method, Constants::METHODARR)) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
         $method = Constants::METHODARR[$method];
 

+ 27 - 15
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php

@@ -2,11 +2,14 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 
 class WorkDay
 {
+    use ArrayEnabled;
+
     /**
      * WORKDAY.
      *
@@ -18,27 +21,37 @@ class WorkDay
      * Excel Function:
      *        WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
      *
-     * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
+     * @param array|mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
      *                                        PHP DateTime object, or a standard date string
-     * @param int $endDays The number of nonweekend and nonholiday days before or after
+     *                         Or can be an array of date values
+     * @param array|int $endDays The number of nonweekend and nonholiday days before or after
      *                                        startDate. A positive value for days yields a future date; a
      *                                        negative value yields a past date.
-     * @param mixed $dateArgs
+     *                         Or can be an array of int values
+     * @param null|mixed $dateArgs An array of dates (such as holidays) to exclude from the calculation
      *
-     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+     * @return array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
      *                        depending on the value of the ReturnDateType flag
+     *         If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
+     *            will also be an array with matching dimensions
      */
     public static function date($startDate, $endDays, ...$dateArgs)
     {
+        if (is_array($startDate) || is_array($endDays)) {
+            return self::evaluateArrayArgumentsSubset(
+                [self::class, __FUNCTION__],
+                2,
+                $startDate,
+                $endDays,
+                ...$dateArgs
+            );
+        }
+
         //    Retrieve the mandatory start date and days that are referenced in the function definition
         try {
             $startDate = Helpers::getDateValue($startDate);
             $endDays = Helpers::validateNumericNull($endDays);
-            $dateArgs = Functions::flattenArray($dateArgs);
-            $holidayArray = [];
-            foreach ($dateArgs as $holidayDate) {
-                $holidayArray[] = Helpers::getDateValue($holidayDate);
-            }
+            $holidayArray = array_map([Helpers::class, 'getDateValue'], Functions::flattenArray($dateArgs));
         } catch (Exception $e) {
             return $e->getMessage();
         }
@@ -64,9 +77,8 @@ class WorkDay
     private static function incrementing(float $startDate, int $endDays, array $holidayArray)
     {
         //    Adjust the start date if it falls over a weekend
-
         $startDoW = self::getWeekDay($startDate, 3);
-        if (self::getWeekDay($startDate, 3) >= 5) {
+        if ($startDoW >= 5) {
             $startDate += 7 - $startDoW;
             --$endDays;
         }
@@ -126,9 +138,8 @@ class WorkDay
     private static function decrementing(float $startDate, int $endDays, array $holidayArray)
     {
         //    Adjust the start date if it falls over a weekend
-
         $startDoW = self::getWeekDay($startDate, 3);
-        if (self::getWeekDay($startDate, 3) >= 5) {
+        if ($startDoW >= 5) {
             $startDate += -$startDoW + 4;
             ++$endDays;
         }
@@ -172,6 +183,7 @@ class WorkDay
             }
             //    Adjust the calculated end date if it falls over a weekend
             $endDoW = self::getWeekDay($endDate, 3);
+            /** int $endDoW */
             if ($endDoW >= 5) {
                 $endDate += -$endDoW + 4;
             }
@@ -182,8 +194,8 @@ class WorkDay
 
     private static function getWeekDay(float $date, int $wd): int
     {
-        $result = Week::day($date, $wd);
+        $result = Functions::scalar(Week::day($date, $wd));
 
-        return is_string($result) ? -1 : $result;
+        return is_int($result) ? $result : -1;
     }
 }

+ 21 - 8
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php

@@ -2,12 +2,16 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
 
 class YearFrac
 {
+    use ArrayEnabled;
+
     /**
      * YEARFRAC.
      *
@@ -23,19 +27,28 @@ class YearFrac
      *
      * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard date string
+     *                         Or can be an array of values
      * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard date string
-     * @param int $method Method used for the calculation
+     *                         Or can be an array of methods
+     * @param array|int $method Method used for the calculation
      *                                        0 or omitted    US (NASD) 30/360
      *                                        1                Actual/actual
      *                                        2                Actual/360
      *                                        3                Actual/365
      *                                        4                European 30/360
+     *                         Or can be an array of methods
      *
-     * @return float|string fraction of the year, or a string containing an error
+     * @return array|float|string fraction of the year, or a string containing an error
+     *         If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
+     *            will also be an array with matching dimensions
      */
     public static function fraction($startDate, $endDate, $method = 0)
     {
+        if (is_array($startDate) || is_array($endDate) || is_array($method)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $startDate, $endDate, $method);
+        }
+
         try {
             $method = (int) Helpers::validateNumericNull($method);
             $sDate = Helpers::getDateValue($startDate);
@@ -50,18 +63,18 @@ class YearFrac
 
         switch ($method) {
             case 0:
-                return Days360::between($startDate, $endDate) / 360;
+                return Functions::scalar(Days360::between($startDate, $endDate)) / 360;
             case 1:
                 return self::method1($startDate, $endDate);
             case 2:
-                return Difference::interval($startDate, $endDate) / 360;
+                return Functions::scalar(Difference::interval($startDate, $endDate)) / 360;
             case 3:
-                return Difference::interval($startDate, $endDate) / 365;
+                return Functions::scalar(Difference::interval($startDate, $endDate)) / 365;
             case 4:
-                return Days360::between($startDate, $endDate, true) / 360;
+                return Functions::scalar(Days360::between($startDate, $endDate, true)) / 360;
         }
 
-        return Functions::NAN();
+        return ExcelError::NAN();
     }
 
     /**
@@ -87,7 +100,7 @@ class YearFrac
 
     private static function method1(float $startDate, float $endDate): float
     {
-        $days = Difference::interval($startDate, $endDate);
+        $days = Functions::scalar(Difference::interval($startDate, $endDate));
         $startYear = (int) DateParts::year($startDate);
         $endYear = (int) DateParts::year($endDate);
         $years = $endYear - $startYear + 1;

+ 209 - 0
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php

@@ -0,0 +1,209 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Calculation\Engine;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Exception;
+
+class ArrayArgumentHelper
+{
+    /**
+     * @var int
+     */
+    protected $indexStart = 0;
+
+    /**
+     * @var array
+     */
+    protected $arguments;
+
+    /**
+     * @var int
+     */
+    protected $argumentCount;
+
+    /**
+     * @var array
+     */
+    protected $rows;
+
+    /**
+     * @var array
+     */
+    protected $columns;
+
+    public function initialise(array $arguments): void
+    {
+        $keys = array_keys($arguments);
+        $this->indexStart = (int) array_shift($keys);
+        $this->rows = $this->rows($arguments);
+        $this->columns = $this->columns($arguments);
+
+        $this->argumentCount = count($arguments);
+        $this->arguments = $this->flattenSingleCellArrays($arguments, $this->rows, $this->columns);
+
+        $this->rows = $this->rows($arguments);
+        $this->columns = $this->columns($arguments);
+
+        if ($this->arrayArguments() > 2) {
+            throw new Exception('Formulae with more than two array arguments are not supported');
+        }
+    }
+
+    public function arguments(): array
+    {
+        return $this->arguments;
+    }
+
+    public function hasArrayArgument(): bool
+    {
+        return $this->arrayArguments() > 0;
+    }
+
+    public function getFirstArrayArgumentNumber(): int
+    {
+        $rowArrays = $this->filterArray($this->rows);
+        $columnArrays = $this->filterArray($this->columns);
+
+        for ($index = $this->indexStart; $index < $this->argumentCount; ++$index) {
+            if (isset($rowArrays[$index]) || isset($columnArrays[$index])) {
+                return ++$index;
+            }
+        }
+
+        return 0;
+    }
+
+    public function getSingleRowVector(): ?int
+    {
+        $rowVectors = $this->getRowVectors();
+
+        return count($rowVectors) === 1 ? array_pop($rowVectors) : null;
+    }
+
+    private function getRowVectors(): array
+    {
+        $rowVectors = [];
+        for ($index = $this->indexStart; $index < ($this->indexStart + $this->argumentCount); ++$index) {
+            if ($this->rows[$index] === 1 && $this->columns[$index] > 1) {
+                $rowVectors[] = $index;
+            }
+        }
+
+        return $rowVectors;
+    }
+
+    public function getSingleColumnVector(): ?int
+    {
+        $columnVectors = $this->getColumnVectors();
+
+        return count($columnVectors) === 1 ? array_pop($columnVectors) : null;
+    }
+
+    private function getColumnVectors(): array
+    {
+        $columnVectors = [];
+        for ($index = $this->indexStart; $index < ($this->indexStart + $this->argumentCount); ++$index) {
+            if ($this->rows[$index] > 1 && $this->columns[$index] === 1) {
+                $columnVectors[] = $index;
+            }
+        }
+
+        return $columnVectors;
+    }
+
+    public function getMatrixPair(): array
+    {
+        for ($i = $this->indexStart; $i < ($this->indexStart + $this->argumentCount - 1); ++$i) {
+            for ($j = $i + 1; $j < $this->argumentCount; ++$j) {
+                if (isset($this->rows[$i], $this->rows[$j])) {
+                    return [$i, $j];
+                }
+            }
+        }
+
+        return [];
+    }
+
+    public function isVector(int $argument): bool
+    {
+        return $this->rows[$argument] === 1 || $this->columns[$argument] === 1;
+    }
+
+    public function isRowVector(int $argument): bool
+    {
+        return $this->rows[$argument] === 1;
+    }
+
+    public function isColumnVector(int $argument): bool
+    {
+        return $this->columns[$argument] === 1;
+    }
+
+    public function rowCount(int $argument): int
+    {
+        return $this->rows[$argument];
+    }
+
+    public function columnCount(int $argument): int
+    {
+        return $this->columns[$argument];
+    }
+
+    private function rows(array $arguments): array
+    {
+        return array_map(
+            function ($argument) {
+                return is_countable($argument) ? count($argument) : 1;
+            },
+            $arguments
+        );
+    }
+
+    private function columns(array $arguments): array
+    {
+        return array_map(
+            function ($argument) {
+                return is_array($argument) && is_array($argument[array_keys($argument)[0]])
+                    ? count($argument[array_keys($argument)[0]])
+                    : 1;
+            },
+            $arguments
+        );
+    }
+
+    public function arrayArguments(): int
+    {
+        $count = 0;
+        foreach (array_keys($this->arguments) as $argument) {
+            if ($this->rows[$argument] > 1 || $this->columns[$argument] > 1) {
+                ++$count;
+            }
+        }
+
+        return $count;
+    }
+
+    private function flattenSingleCellArrays(array $arguments, array $rows, array $columns): array
+    {
+        foreach ($arguments as $index => $argument) {
+            if ($rows[$index] === 1 && $columns[$index] === 1) {
+                while (is_array($argument)) {
+                    $argument = array_pop($argument);
+                }
+                $arguments[$index] = $argument;
+            }
+        }
+
+        return $arguments;
+    }
+
+    private function filterArray(array $array): array
+    {
+        return array_filter(
+            $array,
+            function ($value) {
+                return $value > 1;
+            }
+        );
+    }
+}

+ 175 - 0
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php

@@ -0,0 +1,175 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Calculation\Engine;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+
+class ArrayArgumentProcessor
+{
+    /**
+     * @var ArrayArgumentHelper
+     */
+    private static $arrayArgumentHelper;
+
+    /**
+     * @param mixed ...$arguments
+     */
+    public static function processArguments(
+        ArrayArgumentHelper $arrayArgumentHelper,
+        callable $method,
+        ...$arguments
+    ): array {
+        self::$arrayArgumentHelper = $arrayArgumentHelper;
+
+        if (self::$arrayArgumentHelper->hasArrayArgument() === false) {
+            return [$method(...$arguments)];
+        }
+
+        if (self::$arrayArgumentHelper->arrayArguments() === 1) {
+            $nthArgument = self::$arrayArgumentHelper->getFirstArrayArgumentNumber();
+
+            return self::evaluateNthArgumentAsArray($method, $nthArgument, ...$arguments);
+        }
+
+        $singleRowVectorIndex = self::$arrayArgumentHelper->getSingleRowVector();
+        $singleColumnVectorIndex = self::$arrayArgumentHelper->getSingleColumnVector();
+
+        if ($singleRowVectorIndex !== null && $singleColumnVectorIndex !== null) {
+            // Basic logic for a single row vector and a single column vector
+            return self::evaluateVectorPair($method, $singleRowVectorIndex, $singleColumnVectorIndex, ...$arguments);
+        }
+
+        $matrixPair = self::$arrayArgumentHelper->getMatrixPair();
+        if ($matrixPair !== []) {
+            if (
+                (self::$arrayArgumentHelper->isVector($matrixPair[0]) === true &&
+                    self::$arrayArgumentHelper->isVector($matrixPair[1]) === false) ||
+                (self::$arrayArgumentHelper->isVector($matrixPair[0]) === false &&
+                    self::$arrayArgumentHelper->isVector($matrixPair[1]) === true)
+            ) {
+                // Logic for a matrix and a vector (row or column)
+                return self::evaluateVectorMatrixPair($method, $matrixPair, ...$arguments);
+            }
+            // Logic for matrix/matrix, column vector/column vector or row vector/row vector
+            return self::evaluateMatrixPair($method, $matrixPair, ...$arguments);
+        }
+
+        // Still need to work out the logic for more than two array arguments,
+        // For the moment, we're throwing an Exception when we initialise the ArrayArgumentHelper
+        return ['#VALUE!'];
+    }
+
+    /**
+     * @param mixed ...$arguments
+     */
+    private static function evaluateVectorMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array
+    {
+        $matrix2 = array_pop($matrixIndexes);
+        /** @var array $matrixValues2 */
+        $matrixValues2 = $arguments[$matrix2];
+        $matrix1 = array_pop($matrixIndexes);
+        /** @var array $matrixValues1 */
+        $matrixValues1 = $arguments[$matrix1];
+
+        $rows = min(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2]));
+        $columns = min(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2]));
+
+        if ($rows === 1) {
+            $rows = max(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2]));
+        }
+        if ($columns === 1) {
+            $columns = max(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2]));
+        }
+
+        $result = [];
+        for ($rowIndex = 0; $rowIndex < $rows; ++$rowIndex) {
+            for ($columnIndex = 0; $columnIndex < $columns; ++$columnIndex) {
+                $rowIndex1 = self::$arrayArgumentHelper->isRowVector($matrix1) ? 0 : $rowIndex;
+                $columnIndex1 = self::$arrayArgumentHelper->isColumnVector($matrix1) ? 0 : $columnIndex;
+                $value1 = $matrixValues1[$rowIndex1][$columnIndex1];
+                $rowIndex2 = self::$arrayArgumentHelper->isRowVector($matrix2) ? 0 : $rowIndex;
+                $columnIndex2 = self::$arrayArgumentHelper->isColumnVector($matrix2) ? 0 : $columnIndex;
+                $value2 = $matrixValues2[$rowIndex2][$columnIndex2];
+                $arguments[$matrix1] = $value1;
+                $arguments[$matrix2] = $value2;
+
+                $result[$rowIndex][$columnIndex] = $method(...$arguments);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * @param mixed ...$arguments
+     */
+    private static function evaluateMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array
+    {
+        $matrix2 = array_pop($matrixIndexes);
+        /** @var array $matrixValues2 */
+        $matrixValues2 = $arguments[$matrix2];
+        $matrix1 = array_pop($matrixIndexes);
+        /** @var array $matrixValues1 */
+        $matrixValues1 = $arguments[$matrix1];
+
+        $result = [];
+        foreach ($matrixValues1 as $rowIndex => $row) {
+            foreach ($row as $columnIndex => $value1) {
+                if (isset($matrixValues2[$rowIndex][$columnIndex]) === false) {
+                    continue;
+                }
+
+                $value2 = $matrixValues2[$rowIndex][$columnIndex];
+                $arguments[$matrix1] = $value1;
+                $arguments[$matrix2] = $value2;
+
+                $result[$rowIndex][$columnIndex] = $method(...$arguments);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * @param mixed ...$arguments
+     */
+    private static function evaluateVectorPair(callable $method, int $rowIndex, int $columnIndex, ...$arguments): array
+    {
+        $rowVector = Functions::flattenArray($arguments[$rowIndex]);
+        $columnVector = Functions::flattenArray($arguments[$columnIndex]);
+
+        $result = [];
+        foreach ($columnVector as $column) {
+            $rowResults = [];
+            foreach ($rowVector as $row) {
+                $arguments[$rowIndex] = $row;
+                $arguments[$columnIndex] = $column;
+
+                $rowResults[] = $method(...$arguments);
+            }
+            $result[] = $rowResults;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Note, offset is from 1 (for the first argument) rather than from 0.
+     *
+     * @param mixed ...$arguments
+     */
+    private static function evaluateNthArgumentAsArray(callable $method, int $nthArgument, ...$arguments): array
+    {
+        $values = array_slice($arguments, $nthArgument - 1, 1);
+        /** @var array $values */
+        $values = array_pop($values);
+
+        $result = [];
+        foreach ($values as $value) {
+            $arguments[$nthArgument - 1] = $value;
+            $result[] = $method(...$arguments);
+        }
+
+        return $result;
+    }
+}

+ 223 - 0
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/BranchPruner.php

@@ -0,0 +1,223 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Calculation\Engine;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Exception;
+
+class BranchPruner
+{
+    /**
+     * @var bool
+     */
+    protected $branchPruningEnabled = true;
+
+    /**
+     * Used to generate unique store keys.
+     *
+     * @var int
+     */
+    private $branchStoreKeyCounter = 0;
+
+    /**
+     * currently pending storeKey (last item of the storeKeysStack.
+     *
+     * @var ?string
+     */
+    protected $pendingStoreKey;
+
+    /**
+     * @var string[]
+     */
+    protected $storeKeysStack = [];
+
+    /**
+     * @var bool[]
+     */
+    protected $conditionMap = [];
+
+    /**
+     * @var bool[]
+     */
+    protected $thenMap = [];
+
+    /**
+     * @var bool[]
+     */
+    protected $elseMap = [];
+
+    /**
+     * @var int[]
+     */
+    protected $braceDepthMap = [];
+
+    /**
+     * @var null|string
+     */
+    protected $currentCondition;
+
+    /**
+     * @var null|string
+     */
+    protected $currentOnlyIf;
+
+    /**
+     * @var null|string
+     */
+    protected $currentOnlyIfNot;
+
+    /**
+     * @var null|string
+     */
+    protected $previousStoreKey;
+
+    public function __construct(bool $branchPruningEnabled)
+    {
+        $this->branchPruningEnabled = $branchPruningEnabled;
+    }
+
+    public function clearBranchStore(): void
+    {
+        $this->branchStoreKeyCounter = 0;
+    }
+
+    public function initialiseForLoop(): void
+    {
+        $this->currentCondition = null;
+        $this->currentOnlyIf = null;
+        $this->currentOnlyIfNot = null;
+        $this->previousStoreKey = null;
+        $this->pendingStoreKey = empty($this->storeKeysStack) ? null : end($this->storeKeysStack);
+
+        if ($this->branchPruningEnabled) {
+            $this->initialiseCondition();
+            $this->initialiseThen();
+            $this->initialiseElse();
+        }
+    }
+
+    private function initialiseCondition(): void
+    {
+        if (isset($this->conditionMap[$this->pendingStoreKey]) && $this->conditionMap[$this->pendingStoreKey]) {
+            $this->currentCondition = $this->pendingStoreKey;
+            $stackDepth = count($this->storeKeysStack);
+            if ($stackDepth > 1) {
+                // nested if
+                $this->previousStoreKey = $this->storeKeysStack[$stackDepth - 2];
+            }
+        }
+    }
+
+    private function initialiseThen(): void
+    {
+        if (isset($this->thenMap[$this->pendingStoreKey]) && $this->thenMap[$this->pendingStoreKey]) {
+            $this->currentOnlyIf = $this->pendingStoreKey;
+        } elseif (
+            isset($this->previousStoreKey, $this->thenMap[$this->previousStoreKey])
+            && $this->thenMap[$this->previousStoreKey]
+        ) {
+            $this->currentOnlyIf = $this->previousStoreKey;
+        }
+    }
+
+    private function initialiseElse(): void
+    {
+        if (isset($this->elseMap[$this->pendingStoreKey]) && $this->elseMap[$this->pendingStoreKey]) {
+            $this->currentOnlyIfNot = $this->pendingStoreKey;
+        } elseif (
+            isset($this->previousStoreKey, $this->elseMap[$this->previousStoreKey])
+            && $this->elseMap[$this->previousStoreKey]
+        ) {
+            $this->currentOnlyIfNot = $this->previousStoreKey;
+        }
+    }
+
+    public function decrementDepth(): void
+    {
+        if (!empty($this->pendingStoreKey)) {
+            --$this->braceDepthMap[$this->pendingStoreKey];
+        }
+    }
+
+    public function incrementDepth(): void
+    {
+        if (!empty($this->pendingStoreKey)) {
+            ++$this->braceDepthMap[$this->pendingStoreKey];
+        }
+    }
+
+    public function functionCall(string $functionName): void
+    {
+        if ($this->branchPruningEnabled && ($functionName === 'IF(')) {
+            // we handle a new if
+            $this->pendingStoreKey = $this->getUnusedBranchStoreKey();
+            $this->storeKeysStack[] = $this->pendingStoreKey;
+            $this->conditionMap[$this->pendingStoreKey] = true;
+            $this->braceDepthMap[$this->pendingStoreKey] = 0;
+        } elseif (!empty($this->pendingStoreKey) && array_key_exists($this->pendingStoreKey, $this->braceDepthMap)) {
+            // this is not an if but we go deeper
+            ++$this->braceDepthMap[$this->pendingStoreKey];
+        }
+    }
+
+    public function argumentSeparator(): void
+    {
+        if (!empty($this->pendingStoreKey) && $this->braceDepthMap[$this->pendingStoreKey] === 0) {
+            // We must go to the IF next argument
+            if ($this->conditionMap[$this->pendingStoreKey]) {
+                $this->conditionMap[$this->pendingStoreKey] = false;
+                $this->thenMap[$this->pendingStoreKey] = true;
+            } elseif ($this->thenMap[$this->pendingStoreKey]) {
+                $this->thenMap[$this->pendingStoreKey] = false;
+                $this->elseMap[$this->pendingStoreKey] = true;
+            } elseif ($this->elseMap[$this->pendingStoreKey]) {
+                throw new Exception('Reaching fourth argument of an IF');
+            }
+        }
+    }
+
+    /**
+     * @param mixed $value
+     */
+    public function closingBrace($value): void
+    {
+        if (!empty($this->pendingStoreKey) && $this->braceDepthMap[$this->pendingStoreKey] === -1) {
+            // we are closing an IF(
+            if ($value !== 'IF(') {
+                throw new Exception('Parser bug we should be in an "IF("');
+            }
+
+            if ($this->conditionMap[$this->pendingStoreKey]) {
+                throw new Exception('We should not be expecting a condition');
+            }
+
+            $this->thenMap[$this->pendingStoreKey] = false;
+            $this->elseMap[$this->pendingStoreKey] = false;
+            --$this->braceDepthMap[$this->pendingStoreKey];
+            array_pop($this->storeKeysStack);
+            $this->pendingStoreKey = null;
+        }
+    }
+
+    public function currentCondition(): ?string
+    {
+        return $this->currentCondition;
+    }
+
+    public function currentOnlyIf(): ?string
+    {
+        return $this->currentOnlyIf;
+    }
+
+    public function currentOnlyIfNot(): ?string
+    {
+        return $this->currentOnlyIfNot;
+    }
+
+    private function getUnusedBranchStoreKey(): string
+    {
+        $storeKeyValue = 'storeKey-' . $this->branchStoreKeyCounter;
+        ++$this->branchStoreKeyCounter;
+
+        return $storeKeyValue;
+    }
+}

+ 13 - 11
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php

@@ -48,11 +48,11 @@ class Logger
     /**
      * Enable/Disable Calculation engine logging.
      *
-     * @param bool $pValue
+     * @param bool $writeDebugLog
      */
-    public function setWriteDebugLog($pValue): void
+    public function setWriteDebugLog($writeDebugLog): void
     {
-        $this->writeDebugLog = $pValue;
+        $this->writeDebugLog = $writeDebugLog;
     }
 
     /**
@@ -68,11 +68,11 @@ class Logger
     /**
      * Enable/Disable echoing of debug log information.
      *
-     * @param bool $pValue
+     * @param bool $echoDebugLog
      */
-    public function setEchoDebugLog($pValue): void
+    public function setEchoDebugLog($echoDebugLog): void
     {
-        $this->echoDebugLog = $pValue;
+        $this->echoDebugLog = $echoDebugLog;
     }
 
     /**
@@ -87,18 +87,20 @@ class Logger
 
     /**
      * Write an entry to the calculation engine debug log.
+     *
+     * @param mixed $args
      */
-    public function writeDebugLog(...$args): void
+    public function writeDebugLog(string $message, ...$args): void
     {
         //    Only write the debug log if logging is enabled
         if ($this->writeDebugLog) {
-            $message = implode('', $args);
+            $message = sprintf($message, ...$args);
             $cellReference = implode(' -> ', $this->cellStack->showStack());
             if ($this->echoDebugLog) {
                 echo $cellReference,
-                    ($this->cellStack->count() > 0 ? ' => ' : ''),
-                    $message,
-                    PHP_EOL;
+                ($this->cellStack->count() > 0 ? ' => ' : ''),
+                $message,
+                PHP_EOL;
             }
             $this->debugLog[] = $cellReference .
                 ($this->cellStack->count() > 0 ? ' => ' : '') .

+ 61 - 61
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php

@@ -61,7 +61,7 @@ class Engineering
      *                                If $ord is nonnumeric, BESSELI returns the #VALUE! error value.
      *                                If $ord < 0, BESSELI returns the #NUM! error value.
      *
-     * @return float|string Result, or a string containing an error
+     * @return array|float|string Result, or a string containing an error
      */
     public static function BESSELI($x, $ord)
     {
@@ -86,7 +86,7 @@ class Engineering
      *                                If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
      *                                If $ord < 0, BESSELJ returns the #NUM! error value.
      *
-     * @return float|string Result, or a string containing an error
+     * @return array|float|string Result, or a string containing an error
      */
     public static function BESSELJ($x, $ord)
     {
@@ -112,7 +112,7 @@ class Engineering
      *                                If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
      *                                If $ord < 0, BESSELK returns the #NUM! error value.
      *
-     * @return float|string Result, or a string containing an error
+     * @return array|float|string Result, or a string containing an error
      */
     public static function BESSELK($x, $ord)
     {
@@ -137,7 +137,7 @@ class Engineering
      *                                If $ord is nonnumeric, BESSELY returns the #VALUE! error value.
      *                                If $ord < 0, BESSELY returns the #NUM! error value.
      *
-     * @return float|string Result, or a string containing an error
+     * @return array|float|string Result, or a string containing an error
      */
     public static function BESSELY($x, $ord)
     {
@@ -163,7 +163,7 @@ class Engineering
      *                                If number is not a valid binary number, or if number contains more than
      *                                10 characters (10 bits), BIN2DEC returns the #NUM! error value.
      *
-     * @return string
+     * @return array|string
      */
     public static function BINTODEC($x)
     {
@@ -195,7 +195,7 @@ class Engineering
      *                                If places is nonnumeric, BIN2HEX returns the #VALUE! error value.
      *                                If places is negative, BIN2HEX returns the #NUM! error value.
      *
-     * @return string
+     * @return array|string
      */
     public static function BINTOHEX($x, $places = null)
     {
@@ -227,7 +227,7 @@ class Engineering
      *                                If places is nonnumeric, BIN2OCT returns the #VALUE! error value.
      *                                If places is negative, BIN2OCT returns the #NUM! error value.
      *
-     * @return string
+     * @return array|string
      */
     public static function BINTOOCT($x, $places = null)
     {
@@ -263,7 +263,7 @@ class Engineering
      *                                If places is nonnumeric, DEC2BIN returns the #VALUE! error value.
      *                                If places is zero or negative, DEC2BIN returns the #NUM! error value.
      *
-     * @return string
+     * @return array|string
      */
     public static function DECTOBIN($x, $places = null)
     {
@@ -299,7 +299,7 @@ class Engineering
      *                                If places is nonnumeric, DEC2HEX returns the #VALUE! error value.
      *                                If places is zero or negative, DEC2HEX returns the #NUM! error value.
      *
-     * @return string
+     * @return array|string
      */
     public static function DECTOHEX($x, $places = null)
     {
@@ -335,7 +335,7 @@ class Engineering
      *                                If places is nonnumeric, DEC2OCT returns the #VALUE! error value.
      *                                If places is zero or negative, DEC2OCT returns the #NUM! error value.
      *
-     * @return string
+     * @return array|string
      */
     public static function DECTOOCT($x, $places = null)
     {
@@ -371,7 +371,7 @@ class Engineering
      *                                    If places is nonnumeric, HEX2BIN returns the #VALUE! error value.
      *                                    If places is negative, HEX2BIN returns the #NUM! error value.
      *
-     * @return string
+     * @return array|string
      */
     public static function HEXTOBIN($x, $places = null)
     {
@@ -398,7 +398,7 @@ class Engineering
      *                                If number is not a valid hexadecimal number, HEX2DEC returns the
      *                                #NUM! error value.
      *
-     * @return string
+     * @return array|string
      */
     public static function HEXTODEC($x)
     {
@@ -438,7 +438,7 @@ class Engineering
      *                                    value.
      *                                    If places is negative, HEX2OCT returns the #NUM! error value.
      *
-     * @return string
+     * @return array|string
      */
     public static function HEXTOOCT($x, $places = null)
     {
@@ -480,7 +480,7 @@ class Engineering
      *                                    If places is negative, OCT2BIN returns the #NUM! error
      *                                    value.
      *
-     * @return string
+     * @return array|string
      */
     public static function OCTTOBIN($x, $places = null)
     {
@@ -507,7 +507,7 @@ class Engineering
      *                                If number is not a valid octal number, OCT2DEC returns the
      *                                #NUM! error value.
      *
-     * @return string
+     * @return array|string
      */
     public static function OCTTODEC($x)
     {
@@ -544,7 +544,7 @@ class Engineering
      *                                    If places is nonnumeric, OCT2HEX returns the #VALUE! error value.
      *                                    If places is negative, OCT2HEX returns the #NUM! error value.
      *
-     * @return string
+     * @return array|string
      */
     public static function OCTTOHEX($x, $places = null)
     {
@@ -563,12 +563,12 @@ class Engineering
      *
      * @see Use the COMPLEX() method in the Engineering\Complex class instead
      *
-     * @param float $realNumber the real coefficient of the complex number
-     * @param float $imaginary the imaginary coefficient of the complex number
-     * @param string $suffix The suffix for the imaginary component of the complex number.
+     * @param array|float $realNumber the real coefficient of the complex number
+     * @param array|float $imaginary the imaginary coefficient of the complex number
+     * @param array|string $suffix The suffix for the imaginary component of the complex number.
      *                                        If omitted, the suffix is assumed to be "i".
      *
-     * @return string
+     * @return array|string
      */
     public static function COMPLEX($realNumber = 0.0, $imaginary = 0.0, $suffix = 'i')
     {
@@ -590,7 +590,7 @@ class Engineering
      * @param string $complexNumber the complex number for which you want the imaginary
      *                                         coefficient
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function IMAGINARY($complexNumber)
     {
@@ -611,7 +611,7 @@ class Engineering
      *
      * @param string $complexNumber the complex number for which you want the real coefficient
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function IMREAL($complexNumber)
     {
@@ -632,7 +632,7 @@ class Engineering
      *
      * @param string $complexNumber the complex number for which you want the absolute value
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function IMABS($complexNumber)
     {
@@ -652,9 +652,9 @@ class Engineering
      *
      * @see Use the IMARGUMENT() method in the Engineering\ComplexFunctions class instead
      *
-     * @param string $complexNumber the complex number for which you want the argument theta
+     * @param array|string $complexNumber the complex number for which you want the argument theta
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function IMARGUMENT($complexNumber)
     {
@@ -673,9 +673,9 @@ class Engineering
      *
      * @see Use the IMARGUMENT() method in the Engineering\ComplexFunctions class instead
      *
-     * @param string $complexNumber the complex number for which you want the conjugate
+     * @param array|string $complexNumber the complex number for which you want the conjugate
      *
-     * @return string
+     * @return array|string
      */
     public static function IMCONJUGATE($complexNumber)
     {
@@ -694,9 +694,9 @@ class Engineering
      *
      * @see Use the IMCOS() method in the Engineering\ComplexFunctions class instead
      *
-     * @param string $complexNumber the complex number for which you want the cosine
+     * @param array|string $complexNumber the complex number for which you want the cosine
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function IMCOS($complexNumber)
     {
@@ -715,9 +715,9 @@ class Engineering
      *
      * @see Use the IMCOSH() method in the Engineering\ComplexFunctions class instead
      *
-     * @param string $complexNumber the complex number for which you want the hyperbolic cosine
+     * @param array|string $complexNumber the complex number for which you want the hyperbolic cosine
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function IMCOSH($complexNumber)
     {
@@ -736,9 +736,9 @@ class Engineering
      *
      * @see Use the IMCOT() method in the Engineering\ComplexFunctions class instead
      *
-     * @param string $complexNumber the complex number for which you want the cotangent
+     * @param array|string $complexNumber the complex number for which you want the cotangent
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function IMCOT($complexNumber)
     {
@@ -757,9 +757,9 @@ class Engineering
      *
      * @see Use the IMCSC() method in the Engineering\ComplexFunctions class instead
      *
-     * @param string $complexNumber the complex number for which you want the cosecant
+     * @param array|string $complexNumber the complex number for which you want the cosecant
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function IMCSC($complexNumber)
     {
@@ -778,9 +778,9 @@ class Engineering
      *
      * @see Use the IMCSCH() method in the Engineering\ComplexFunctions class instead
      *
-     * @param string $complexNumber the complex number for which you want the hyperbolic cosecant
+     * @param array|string $complexNumber the complex number for which you want the hyperbolic cosecant
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function IMCSCH($complexNumber)
     {
@@ -801,7 +801,7 @@ class Engineering
      *
      * @param string $complexNumber the complex number for which you want the sine
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function IMSIN($complexNumber)
     {
@@ -822,7 +822,7 @@ class Engineering
      *
      * @param string $complexNumber the complex number for which you want the hyperbolic sine
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function IMSINH($complexNumber)
     {
@@ -843,7 +843,7 @@ class Engineering
      *
      * @param string $complexNumber the complex number for which you want the secant
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function IMSEC($complexNumber)
     {
@@ -864,7 +864,7 @@ class Engineering
      *
      * @param string $complexNumber the complex number for which you want the hyperbolic secant
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function IMSECH($complexNumber)
     {
@@ -885,7 +885,7 @@ class Engineering
      *
      * @param string $complexNumber the complex number for which you want the tangent
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function IMTAN($complexNumber)
     {
@@ -906,7 +906,7 @@ class Engineering
      *
      * @param string $complexNumber the complex number for which you want the square root
      *
-     * @return string
+     * @return array|string
      */
     public static function IMSQRT($complexNumber)
     {
@@ -927,7 +927,7 @@ class Engineering
      *
      * @param string $complexNumber the complex number for which you want the natural logarithm
      *
-     * @return string
+     * @return array|string
      */
     public static function IMLN($complexNumber)
     {
@@ -948,7 +948,7 @@ class Engineering
      *
      * @param string $complexNumber the complex number for which you want the common logarithm
      *
-     * @return string
+     * @return array|string
      */
     public static function IMLOG10($complexNumber)
     {
@@ -969,7 +969,7 @@ class Engineering
      *
      * @param string $complexNumber the complex number for which you want the base-2 logarithm
      *
-     * @return string
+     * @return array|string
      */
     public static function IMLOG2($complexNumber)
     {
@@ -990,7 +990,7 @@ class Engineering
      *
      * @param string $complexNumber the complex number for which you want the exponential
      *
-     * @return string
+     * @return array|string
      */
     public static function IMEXP($complexNumber)
     {
@@ -1012,7 +1012,7 @@ class Engineering
      * @param string $complexNumber the complex number you want to raise to a power
      * @param float $realNumber the power to which you want to raise the complex number
      *
-     * @return string
+     * @return array|string
      */
     public static function IMPOWER($complexNumber, $realNumber)
     {
@@ -1034,7 +1034,7 @@ class Engineering
      * @param string $complexDividend the complex numerator or dividend
      * @param string $complexDivisor the complex denominator or divisor
      *
-     * @return string
+     * @return array|string
      */
     public static function IMDIV($complexDividend, $complexDivisor)
     {
@@ -1056,7 +1056,7 @@ class Engineering
      * @param string $complexNumber1 the complex number from which to subtract complexNumber2
      * @param string $complexNumber2 the complex number to subtract from complexNumber1
      *
-     * @return string
+     * @return array|string
      */
     public static function IMSUB($complexNumber1, $complexNumber2)
     {
@@ -1123,7 +1123,7 @@ class Engineering
      * @param float $a the first number
      * @param float $b The second number. If omitted, b is assumed to be zero.
      *
-     * @return int|string (string in the event of an error)
+     * @return array|int|string (string in the event of an error)
      */
     public static function DELTA($a, $b = 0)
     {
@@ -1147,7 +1147,7 @@ class Engineering
      * @param float $number the value to test against step
      * @param float $step The threshold value. If you omit a value for step, GESTEP uses zero.
      *
-     * @return int|string (string in the event of an error)
+     * @return array|int|string (string in the event of an error)
      */
     public static function GESTEP($number, $step = 0)
     {
@@ -1169,7 +1169,7 @@ class Engineering
      * @param int $number1
      * @param int $number2
      *
-     * @return int|string
+     * @return array|int|string
      */
     public static function BITAND($number1, $number2)
     {
@@ -1191,7 +1191,7 @@ class Engineering
      * @param int $number1
      * @param int $number2
      *
-     * @return int|string
+     * @return array|int|string
      */
     public static function BITOR($number1, $number2)
     {
@@ -1213,7 +1213,7 @@ class Engineering
      * @param int $number1
      * @param int $number2
      *
-     * @return int|string
+     * @return array|int|string
      */
     public static function BITXOR($number1, $number2)
     {
@@ -1235,7 +1235,7 @@ class Engineering
      * @param int $number
      * @param int $shiftAmount
      *
-     * @return int|string
+     * @return array|float|int|string
      */
     public static function BITLSHIFT($number, $shiftAmount)
     {
@@ -1257,7 +1257,7 @@ class Engineering
      * @param int $number
      * @param int $shiftAmount
      *
-     * @return int|string
+     * @return array|float|int|string
      */
     public static function BITRSHIFT($number, $shiftAmount)
     {
@@ -1285,7 +1285,7 @@ class Engineering
      * @param float $upper upper bound for integrating ERF.
      *                                If omitted, ERF integrates between zero and lower_limit
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function ERF($lower, $upper = null)
     {
@@ -1306,7 +1306,7 @@ class Engineering
      *
      * @param float $limit bound for integrating ERF
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function ERFPRECISE($limit)
     {
@@ -1332,7 +1332,7 @@ class Engineering
      *
      * @param float $x The lower bound for integrating ERFC
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function ERFC($x)
     {
@@ -1437,7 +1437,7 @@ class Engineering
      * @param string $fromUOM the units for value
      * @param string $toUOM the units for the result
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function CONVERTUOM($value, $fromUOM, $toUOM)
     {

+ 14 - 6
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php

@@ -2,11 +2,14 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class BesselI
 {
+    use ArrayEnabled;
+
     /**
      * BESSELI.
      *
@@ -21,17 +24,22 @@ class BesselI
      *
      * @param mixed $x A float value at which to evaluate the function.
      *                                If x is nonnumeric, BESSELI returns the #VALUE! error value.
+     *                      Or can be an array of values
      * @param mixed $ord The integer order of the Bessel function.
      *                                If ord is not an integer, it is truncated.
      *                                If $ord is nonnumeric, BESSELI returns the #VALUE! error value.
      *                                If $ord < 0, BESSELI returns the #NUM! error value.
+     *                      Or can be an array of values
      *
-     * @return float|string Result, or a string containing an error
+     * @return array|float|string Result, or a string containing an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function BESSELI($x, $ord)
     {
-        $x = Functions::flattenSingleValue($x);
-        $ord = Functions::flattenSingleValue($ord);
+        if (is_array($x) || is_array($ord)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
+        }
 
         try {
             $x = EngineeringValidations::validateFloat($x);
@@ -41,12 +49,12 @@ class BesselI
         }
 
         if ($ord < 0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         $fResult = self::calculate($x, $ord);
 
-        return (is_nan($fResult)) ? Functions::NAN() : $fResult;
+        return (is_nan($fResult)) ? ExcelError::NAN() : $fResult;
     }
 
     private static function calculate(float $x, int $ord): float

+ 14 - 6
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php

@@ -2,11 +2,14 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class BesselJ
 {
+    use ArrayEnabled;
+
     /**
      * BESSELJ.
      *
@@ -20,17 +23,22 @@ class BesselJ
      *
      * @param mixed $x A float value at which to evaluate the function.
      *                                If x is nonnumeric, BESSELJ returns the #VALUE! error value.
+     *                      Or can be an array of values
      * @param mixed $ord The integer order of the Bessel function.
      *                       If ord is not an integer, it is truncated.
      *                                If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
      *                                If $ord < 0, BESSELJ returns the #NUM! error value.
+     *                      Or can be an array of values
      *
-     * @return float|string Result, or a string containing an error
+     * @return array|float|string Result, or a string containing an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function BESSELJ($x, $ord)
     {
-        $x = Functions::flattenSingleValue($x);
-        $ord = Functions::flattenSingleValue($ord);
+        if (is_array($x) || is_array($ord)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
+        }
 
         try {
             $x = EngineeringValidations::validateFloat($x);
@@ -40,12 +48,12 @@ class BesselJ
         }
 
         if ($ord < 0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         $fResult = self::calculate($x, $ord);
 
-        return (is_nan($fResult)) ? Functions::NAN() : $fResult;
+        return (is_nan($fResult)) ? ExcelError::NAN() : $fResult;
     }
 
     private static function calculate(float $x, int $ord): float

+ 32 - 8
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php

@@ -2,11 +2,15 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class BesselK
 {
+    use ArrayEnabled;
+
     /**
      * BESSELK.
      *
@@ -18,17 +22,22 @@ class BesselK
      *
      * @param mixed $x A float value at which to evaluate the function.
      *                                If x is nonnumeric, BESSELK returns the #VALUE! error value.
+     *                      Or can be an array of values
      * @param mixed $ord The integer order of the Bessel function.
      *                       If ord is not an integer, it is truncated.
      *                                If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
      *                       If $ord < 0, BESSELKI returns the #NUM! error value.
+     *                      Or can be an array of values
      *
-     * @return float|string Result, or a string containing an error
+     * @return array|float|string Result, or a string containing an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function BESSELK($x, $ord)
     {
-        $x = Functions::flattenSingleValue($x);
-        $ord = Functions::flattenSingleValue($ord);
+        if (is_array($x) || is_array($ord)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
+        }
 
         try {
             $x = EngineeringValidations::validateFloat($x);
@@ -38,12 +47,12 @@ class BesselK
         }
 
         if (($ord < 0) || ($x <= 0.0)) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         $fBk = self::calculate($x, $ord);
 
-        return (is_nan($fBk)) ? Functions::NAN() : $fBk;
+        return (is_nan($fBk)) ? ExcelError::NAN() : $fBk;
     }
 
     private static function calculate(float $x, int $ord): float
@@ -59,13 +68,28 @@ class BesselK
         return self::besselK2($x, $ord);
     }
 
+    /**
+     * Mollify Phpstan.
+     *
+     * @codeCoverageIgnore
+     */
+    private static function callBesselI(float $x, int $ord): float
+    {
+        $rslt = BesselI::BESSELI($x, $ord);
+        if (!is_float($rslt)) {
+            throw new Exception('Unexpected array or string');
+        }
+
+        return $rslt;
+    }
+
     private static function besselK0(float $x): float
     {
         if ($x <= 2) {
             $fNum2 = $x * 0.5;
             $y = ($fNum2 * $fNum2);
 
-            return -log($fNum2) * BesselI::BESSELI($x, 0) +
+            return -log($fNum2) * self::callBesselI($x, 0) +
                 (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
                                     (0.10750e-3 + $y * 0.74e-5))))));
         }
@@ -83,7 +107,7 @@ class BesselK
             $fNum2 = $x * 0.5;
             $y = ($fNum2 * $fNum2);
 
-            return log($fNum2) * BesselI::BESSELI($x, 1) +
+            return log($fNum2) * self::callBesselI($x, 1) +
                 (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
                                     (-0.110404e-2 + $y * (-0.4686e-4))))))) / $x;
         }
@@ -95,7 +119,7 @@ class BesselK
                                 (0.325614e-2 + $y * (-0.68245e-3)))))));
     }
 
-    private static function besselK2(float $x, int $ord)
+    private static function besselK2(float $x, int $ord): float
     {
         $fTox = 2 / $x;
         $fBkm = self::besselK0($x);

+ 31 - 8
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php

@@ -2,11 +2,14 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class BesselY
 {
+    use ArrayEnabled;
+
     /**
      * BESSELY.
      *
@@ -17,17 +20,22 @@ class BesselY
      *
      * @param mixed $x A float value at which to evaluate the function.
      *                   If x is nonnumeric, BESSELY returns the #VALUE! error value.
+     *                      Or can be an array of values
      * @param mixed $ord The integer order of the Bessel function.
      *                       If ord is not an integer, it is truncated.
      *                       If $ord is nonnumeric, BESSELY returns the #VALUE! error value.
      *                       If $ord < 0, BESSELY returns the #NUM! error value.
+     *                      Or can be an array of values
      *
-     * @return float|string Result, or a string containing an error
+     * @return array|float|string Result, or a string containing an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function BESSELY($x, $ord)
     {
-        $x = Functions::flattenSingleValue($x);
-        $ord = Functions::flattenSingleValue($ord);
+        if (is_array($x) || is_array($ord)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
+        }
 
         try {
             $x = EngineeringValidations::validateFloat($x);
@@ -37,12 +45,12 @@ class BesselY
         }
 
         if (($ord < 0) || ($x <= 0.0)) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         $fBy = self::calculate($x, $ord);
 
-        return (is_nan($fBy)) ? Functions::NAN() : $fBy;
+        return (is_nan($fBy)) ? ExcelError::NAN() : $fBy;
     }
 
     private static function calculate(float $x, int $ord): float
@@ -58,6 +66,21 @@ class BesselY
         return self::besselY2($x, $ord);
     }
 
+    /**
+     * Mollify Phpstan.
+     *
+     * @codeCoverageIgnore
+     */
+    private static function callBesselJ(float $x, int $ord): float
+    {
+        $rslt = BesselJ::BESSELJ($x, $ord);
+        if (!is_float($rslt)) {
+            throw new Exception('Unexpected array or string');
+        }
+
+        return $rslt;
+    }
+
     private static function besselY0(float $x): float
     {
         if ($x < 8.0) {
@@ -67,7 +90,7 @@ class BesselY
             $ans2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y *
                         (47447.26470 + $y * (226.1030244 + $y))));
 
-            return $ans1 / $ans2 + 0.636619772 * BesselJ::BESSELJ($x, 0) * log($x);
+            return $ans1 / $ans2 + 0.636619772 * self::callBesselJ($x, 0) * log($x);
         }
 
         $z = 8.0 / $x;
@@ -89,7 +112,7 @@ class BesselY
             $ans2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
                             (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
 
-            return ($ans1 / $ans2) + 0.636619772 * (BesselJ::BESSELJ($x, 1) * log($x) - 1 / $x);
+            return ($ans1 / $ans2) + 0.636619772 * (self::callBesselJ($x, 1) * log($x) - 1 / $x);
         }
 
         $z = 8.0 / $x;

+ 75 - 27
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php

@@ -2,11 +2,15 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class BitWise
 {
+    use ArrayEnabled;
+
     const SPLIT_DIVISOR = 2 ** 24;
 
     /**
@@ -27,13 +31,21 @@ class BitWise
      * Excel Function:
      *        BITAND(number1, number2)
      *
-     * @param int $number1
-     * @param int $number2
+     * @param array|int $number1
+     *                      Or can be an array of values
+     * @param array|int $number2
+     *                      Or can be an array of values
      *
-     * @return int|string
+     * @return array|int|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function BITAND($number1, $number2)
     {
+        if (is_array($number1) || is_array($number2)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
+        }
+
         try {
             $number1 = self::validateBitwiseArgument($number1);
             $number2 = self::validateBitwiseArgument($number2);
@@ -54,13 +66,21 @@ class BitWise
      * Excel Function:
      *        BITOR(number1, number2)
      *
-     * @param int $number1
-     * @param int $number2
+     * @param array|int $number1
+     *                      Or can be an array of values
+     * @param array|int $number2
+     *                      Or can be an array of values
      *
-     * @return int|string
+     * @return array|int|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function BITOR($number1, $number2)
     {
+        if (is_array($number1) || is_array($number2)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
+        }
+
         try {
             $number1 = self::validateBitwiseArgument($number1);
             $number2 = self::validateBitwiseArgument($number2);
@@ -82,13 +102,21 @@ class BitWise
      * Excel Function:
      *        BITXOR(number1, number2)
      *
-     * @param int $number1
-     * @param int $number2
+     * @param array|int $number1
+     *                      Or can be an array of values
+     * @param array|int $number2
+     *                      Or can be an array of values
      *
-     * @return int|string
+     * @return array|int|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function BITXOR($number1, $number2)
     {
+        if (is_array($number1) || is_array($number2)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
+        }
+
         try {
             $number1 = self::validateBitwiseArgument($number1);
             $number2 = self::validateBitwiseArgument($number2);
@@ -110,13 +138,21 @@ class BitWise
      * Excel Function:
      *        BITLSHIFT(number, shift_amount)
      *
-     * @param int $number
-     * @param int $shiftAmount
+     * @param array|int $number
+     *                      Or can be an array of values
+     * @param array|int $shiftAmount
+     *                      Or can be an array of values
      *
-     * @return float|int|string
+     * @return array|float|int|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function BITLSHIFT($number, $shiftAmount)
     {
+        if (is_array($number) || is_array($shiftAmount)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
+        }
+
         try {
             $number = self::validateBitwiseArgument($number);
             $shiftAmount = self::validateShiftAmount($shiftAmount);
@@ -126,7 +162,7 @@ class BitWise
 
         $result = floor($number * (2 ** $shiftAmount));
         if ($result > 2 ** 48 - 1) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return $result;
@@ -140,13 +176,21 @@ class BitWise
      * Excel Function:
      *        BITRSHIFT(number, shift_amount)
      *
-     * @param int $number
-     * @param int $shiftAmount
+     * @param array|int $number
+     *                      Or can be an array of values
+     * @param array|int $shiftAmount
+     *                      Or can be an array of values
      *
-     * @return float|int|string
+     * @return array|float|int|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function BITRSHIFT($number, $shiftAmount)
     {
+        if (is_array($number) || is_array($shiftAmount)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
+        }
+
         try {
             $number = self::validateBitwiseArgument($number);
             $shiftAmount = self::validateShiftAmount($shiftAmount);
@@ -156,7 +200,7 @@ class BitWise
 
         $result = floor($number / (2 ** $shiftAmount));
         if ($result > 2 ** 48 - 1) { // possible because shiftAmount can be negative
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return $result;
@@ -167,25 +211,26 @@ class BitWise
      *
      * @param mixed $value
      *
-     * @return float|int
+     * @return float
      */
     private static function validateBitwiseArgument($value)
     {
-        self::nullFalseTrueToNumber($value);
+        $value = self::nullFalseTrueToNumber($value);
 
         if (is_numeric($value)) {
+            $value = (float) $value;
             if ($value == floor($value)) {
                 if (($value > 2 ** 48 - 1) || ($value < 0)) {
-                    throw new Exception(Functions::NAN());
+                    throw new Exception(ExcelError::NAN());
                 }
 
                 return floor($value);
             }
 
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
-        throw new Exception(Functions::VALUE());
+        throw new Exception(ExcelError::VALUE());
     }
 
     /**
@@ -197,31 +242,34 @@ class BitWise
      */
     private static function validateShiftAmount($value)
     {
-        self::nullFalseTrueToNumber($value);
+        $value = self::nullFalseTrueToNumber($value);
 
         if (is_numeric($value)) {
             if (abs($value) > 53) {
-                throw new Exception(Functions::NAN());
+                throw new Exception(ExcelError::NAN());
             }
 
             return (int) $value;
         }
 
-        throw new Exception(Functions::VALUE());
+        throw new Exception(ExcelError::VALUE());
     }
 
     /**
      * Many functions accept null/false/true argument treated as 0/0/1.
      *
      * @param mixed $number
+     *
+     * @return mixed
      */
-    public static function nullFalseTrueToNumber(&$number): void
+    private static function nullFalseTrueToNumber(&$number)
     {
-        $number = Functions::flattenSingleValue($number);
         if ($number === null) {
             $number = 0;
         } elseif (is_bool($number)) {
             $number = (int) $number;
         }
+
+        return $number;
     }
 }

+ 27 - 15
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php

@@ -2,11 +2,13 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 
 class Compare
 {
+    use ArrayEnabled;
+
     /**
      * DELTA.
      *
@@ -18,15 +20,20 @@ class Compare
      *        functions you calculate the count of equal pairs. This function is also known as the
      *        Kronecker Delta function.
      *
-     * @param float $a the first number
-     * @param float $b The second number. If omitted, b is assumed to be zero.
+     * @param array|float $a the first number
+     *                      Or can be an array of values
+     * @param array|float $b The second number. If omitted, b is assumed to be zero.
+     *                      Or can be an array of values
      *
-     * @return int|string (string in the event of an error)
+     * @return array|int|string (string in the event of an error)
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
-    public static function DELTA($a, $b = 0)
+    public static function DELTA($a, $b = 0.0)
     {
-        $a = Functions::flattenSingleValue($a);
-        $b = Functions::flattenSingleValue($b);
+        if (is_array($a) || is_array($b)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $a, $b);
+        }
 
         try {
             $a = EngineeringValidations::validateFloat($a);
@@ -35,7 +42,7 @@ class Compare
             return $e->getMessage();
         }
 
-        return (int) ($a == $b);
+        return (int) (abs($a - $b) < 1.0e-15);
     }
 
     /**
@@ -48,19 +55,24 @@ class Compare
      *    Use this function to filter a set of values. For example, by summing several GESTEP
      *        functions you calculate the count of values that exceed a threshold.
      *
-     * @param float $number the value to test against step
-     * @param float $step The threshold value. If you omit a value for step, GESTEP uses zero.
+     * @param array|float $number the value to test against step
+     *                      Or can be an array of values
+     * @param null|array|float $step The threshold value. If you omit a value for step, GESTEP uses zero.
+     *                      Or can be an array of values
      *
-     * @return int|string (string in the event of an error)
+     * @return array|int|string (string in the event of an error)
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
-    public static function GESTEP($number, $step = 0)
+    public static function GESTEP($number, $step = 0.0)
     {
-        $number = Functions::flattenSingleValue($number);
-        $step = Functions::flattenSingleValue($step);
+        if (is_array($number) || is_array($step)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $step);
+        }
 
         try {
             $number = EngineeringValidations::validateFloat($number);
-            $step = EngineeringValidations::validateFloat($step);
+            $step = EngineeringValidations::validateFloat($step ?? 0.0);
         } catch (Exception $e) {
             return $e->getMessage();
         }

+ 36 - 14
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php

@@ -4,11 +4,14 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
 use Complex\Complex as ComplexObject;
 use Complex\Exception as ComplexException;
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Complex
 {
+    use ArrayEnabled;
+
     /**
      * COMPLEX.
      *
@@ -18,17 +21,26 @@ class Complex
      *        COMPLEX(realNumber,imaginary[,suffix])
      *
      * @param mixed $realNumber the real float coefficient of the complex number
+     *                      Or can be an array of values
      * @param mixed $imaginary the imaginary float coefficient of the complex number
+     *                      Or can be an array of values
      * @param mixed $suffix The character suffix for the imaginary component of the complex number.
      *                          If omitted, the suffix is assumed to be "i".
+     *                      Or can be an array of values
      *
-     * @return string
+     * @return array|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function COMPLEX($realNumber = 0.0, $imaginary = 0.0, $suffix = 'i')
     {
-        $realNumber = ($realNumber === null) ? 0.0 : Functions::flattenSingleValue($realNumber);
-        $imaginary = ($imaginary === null) ? 0.0 : Functions::flattenSingleValue($imaginary);
-        $suffix = ($suffix === null) ? 'i' : Functions::flattenSingleValue($suffix);
+        if (is_array($realNumber) || is_array($imaginary) || is_array($suffix)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $realNumber, $imaginary, $suffix);
+        }
+
+        $realNumber = $realNumber ?? 0.0;
+        $imaginary = $imaginary ?? 0.0;
+        $suffix = $suffix ?? 'i';
 
         try {
             $realNumber = EngineeringValidations::validateFloat($realNumber);
@@ -43,7 +55,7 @@ class Complex
             return (string) $complex;
         }
 
-        return Functions::VALUE();
+        return ExcelError::VALUE();
     }
 
     /**
@@ -54,19 +66,24 @@ class Complex
      * Excel Function:
      *        IMAGINARY(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the imaginary
+     * @param array|string $complexNumber the complex number for which you want the imaginary
      *                                         coefficient
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string (string if an error)
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMAGINARY($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return $complex->getImaginary();
@@ -80,18 +97,23 @@ class Complex
      * Excel Function:
      *        IMREAL(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the real coefficient
+     * @param array|string $complexNumber the complex number for which you want the real coefficient
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string (string if an error)
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMREAL($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return $complex->getReal();

+ 184 - 86
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php

@@ -4,10 +4,13 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
 use Complex\Complex as ComplexObject;
 use Complex\Exception as ComplexException;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class ComplexFunctions
 {
+    use ArrayEnabled;
+
     /**
      * IMABS.
      *
@@ -16,18 +19,23 @@ class ComplexFunctions
      * Excel Function:
      *        IMABS(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the absolute value
+     * @param array|string $complexNumber the complex number for which you want the absolute value
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMABS($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return $complex->abs();
@@ -42,22 +50,27 @@ class ComplexFunctions
      * Excel Function:
      *        IMARGUMENT(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the argument theta
+     * @param array|string $complexNumber the complex number for which you want the argument theta
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMARGUMENT($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
-            return Functions::DIV0();
+            return ExcelError::DIV0();
         }
 
         return $complex->argument();
@@ -71,18 +84,23 @@ class ComplexFunctions
      * Excel Function:
      *        IMCONJUGATE(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the conjugate
+     * @param array|string $complexNumber the complex number for which you want the conjugate
+     *                      Or can be an array of values
      *
-     * @return string
+     * @return array|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMCONJUGATE($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $complex->conjugate();
@@ -96,18 +114,23 @@ class ComplexFunctions
      * Excel Function:
      *        IMCOS(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the cosine
+     * @param array|string $complexNumber the complex number for which you want the cosine
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMCOS($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $complex->cos();
@@ -121,18 +144,23 @@ class ComplexFunctions
      * Excel Function:
      *        IMCOSH(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the hyperbolic cosine
+     * @param array|string $complexNumber the complex number for which you want the hyperbolic cosine
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMCOSH($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $complex->cosh();
@@ -146,18 +174,23 @@ class ComplexFunctions
      * Excel Function:
      *        IMCOT(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the cotangent
+     * @param array|string $complexNumber the complex number for which you want the cotangent
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMCOT($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $complex->cot();
@@ -171,18 +204,23 @@ class ComplexFunctions
      * Excel Function:
      *        IMCSC(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the cosecant
+     * @param array|string $complexNumber the complex number for which you want the cosecant
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMCSC($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $complex->csc();
@@ -196,18 +234,23 @@ class ComplexFunctions
      * Excel Function:
      *        IMCSCH(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the hyperbolic cosecant
+     * @param array|string $complexNumber the complex number for which you want the hyperbolic cosecant
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMCSCH($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $complex->csch();
@@ -221,18 +264,23 @@ class ComplexFunctions
      * Excel Function:
      *        IMSIN(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the sine
+     * @param array|string $complexNumber the complex number for which you want the sine
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMSIN($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $complex->sin();
@@ -246,18 +294,23 @@ class ComplexFunctions
      * Excel Function:
      *        IMSINH(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the hyperbolic sine
+     * @param array|string $complexNumber the complex number for which you want the hyperbolic sine
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMSINH($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $complex->sinh();
@@ -271,18 +324,23 @@ class ComplexFunctions
      * Excel Function:
      *        IMSEC(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the secant
+     * @param array|string $complexNumber the complex number for which you want the secant
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMSEC($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $complex->sec();
@@ -296,18 +354,23 @@ class ComplexFunctions
      * Excel Function:
      *        IMSECH(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the hyperbolic secant
+     * @param array|string $complexNumber the complex number for which you want the hyperbolic secant
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMSECH($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $complex->sech();
@@ -321,18 +384,23 @@ class ComplexFunctions
      * Excel Function:
      *        IMTAN(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the tangent
+     * @param array|string $complexNumber the complex number for which you want the tangent
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMTAN($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $complex->tan();
@@ -346,22 +414,27 @@ class ComplexFunctions
      * Excel Function:
      *        IMSQRT(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the square root
+     * @param array|string $complexNumber the complex number for which you want the square root
+     *                      Or can be an array of values
      *
-     * @return string
+     * @return array|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMSQRT($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         $theta = self::IMARGUMENT($complexNumber);
-        if ($theta === Functions::DIV0()) {
+        if ($theta === ExcelError::DIV0()) {
             return '0';
         }
 
@@ -376,22 +449,27 @@ class ComplexFunctions
      * Excel Function:
      *        IMLN(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the natural logarithm
+     * @param array|string $complexNumber the complex number for which you want the natural logarithm
+     *                      Or can be an array of values
      *
-     * @return string
+     * @return array|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMLN($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $complex->ln();
@@ -405,22 +483,27 @@ class ComplexFunctions
      * Excel Function:
      *        IMLOG10(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the common logarithm
+     * @param array|string $complexNumber the complex number for which you want the common logarithm
+     *                      Or can be an array of values
      *
-     * @return string
+     * @return array|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMLOG10($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $complex->log10();
@@ -434,22 +517,27 @@ class ComplexFunctions
      * Excel Function:
      *        IMLOG2(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the base-2 logarithm
+     * @param array|string $complexNumber the complex number for which you want the base-2 logarithm
+     *                      Or can be an array of values
      *
-     * @return string
+     * @return array|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMLOG2($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $complex->log2();
@@ -463,18 +551,23 @@ class ComplexFunctions
      * Excel Function:
      *        IMEXP(complexNumber)
      *
-     * @param string $complexNumber the complex number for which you want the exponential
+     * @param array|string $complexNumber the complex number for which you want the exponential
+     *                      Or can be an array of values
      *
-     * @return string
+     * @return array|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMEXP($complexNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
+        if (is_array($complexNumber)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $complex->exp();
@@ -488,26 +581,31 @@ class ComplexFunctions
      * Excel Function:
      *        IMPOWER(complexNumber,realNumber)
      *
-     * @param string $complexNumber the complex number you want to raise to a power
-     * @param float $realNumber the power to which you want to raise the complex number
+     * @param array|string $complexNumber the complex number you want to raise to a power
+     *                      Or can be an array of values
+     * @param array|float|int|string $realNumber the power to which you want to raise the complex number
+     *                      Or can be an array of values
      *
-     * @return string
+     * @return array|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMPOWER($complexNumber, $realNumber)
     {
-        $complexNumber = Functions::flattenSingleValue($complexNumber);
-        $realNumber = Functions::flattenSingleValue($realNumber);
+        if (is_array($complexNumber) || is_array($realNumber)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexNumber, $realNumber);
+        }
 
         try {
             $complex = new ComplexObject($complexNumber);
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         if (!is_numeric($realNumber)) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
 
-        return (string) $complex->pow($realNumber);
+        return (string) $complex->pow((float) $realNumber);
     }
 }

+ 28 - 14
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php

@@ -4,10 +4,14 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
 use Complex\Complex as ComplexObject;
 use Complex\Exception as ComplexException;
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class ComplexOperations
 {
+    use ArrayEnabled;
+
     /**
      * IMDIV.
      *
@@ -16,20 +20,25 @@ class ComplexOperations
      * Excel Function:
      *        IMDIV(complexDividend,complexDivisor)
      *
-     * @param string $complexDividend the complex numerator or dividend
-     * @param string $complexDivisor the complex denominator or divisor
+     * @param array|string $complexDividend the complex numerator or dividend
+     *                      Or can be an array of values
+     * @param array|string $complexDivisor the complex denominator or divisor
+     *                      Or can be an array of values
      *
-     * @return string
+     * @return array|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMDIV($complexDividend, $complexDivisor)
     {
-        $complexDividend = Functions::flattenSingleValue($complexDividend);
-        $complexDivisor = Functions::flattenSingleValue($complexDivisor);
+        if (is_array($complexDividend) || is_array($complexDivisor)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexDividend, $complexDivisor);
+        }
 
         try {
             return (string) (new ComplexObject($complexDividend))->divideby(new ComplexObject($complexDivisor));
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
     }
 
@@ -41,20 +50,25 @@ class ComplexOperations
      * Excel Function:
      *        IMSUB(complexNumber1,complexNumber2)
      *
-     * @param string $complexNumber1 the complex number from which to subtract complexNumber2
-     * @param string $complexNumber2 the complex number to subtract from complexNumber1
+     * @param array|string $complexNumber1 the complex number from which to subtract complexNumber2
+     *                      Or can be an array of values
+     * @param array|string $complexNumber2 the complex number to subtract from complexNumber1
+     *                      Or can be an array of values
      *
-     * @return string
+     * @return array|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function IMSUB($complexNumber1, $complexNumber2)
     {
-        $complexNumber1 = Functions::flattenSingleValue($complexNumber1);
-        $complexNumber2 = Functions::flattenSingleValue($complexNumber2);
+        if (is_array($complexNumber1) || is_array($complexNumber2)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexNumber1, $complexNumber2);
+        }
 
         try {
             return (string) (new ComplexObject($complexNumber1))->subtract(new ComplexObject($complexNumber2));
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
     }
 
@@ -82,7 +96,7 @@ class ComplexOperations
                 $returnValue = $returnValue->add(new ComplexObject($complex));
             }
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $returnValue;
@@ -112,7 +126,7 @@ class ComplexOperations
                 $returnValue = $returnValue->multiply(new ComplexObject($complex));
             }
         } catch (ComplexException $e) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (string) $returnValue;

+ 9 - 5
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php

@@ -2,16 +2,20 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
-class ConvertBase
+abstract class ConvertBase
 {
+    use ArrayEnabled;
+
     protected static function validateValue($value): string
     {
         if (is_bool($value)) {
             if (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE) {
-                throw new Exception(Functions::VALUE());
+                throw new Exception(ExcelError::VALUE());
             }
             $value = (int) $value;
         }
@@ -33,13 +37,13 @@ class ConvertBase
 
         if (is_numeric($places)) {
             if ($places < 0 || $places > 10) {
-                throw new Exception(Functions::NAN());
+                throw new Exception(ExcelError::NAN());
             }
 
             return (int) $places;
         }
 
-        throw new Exception(Functions::VALUE());
+        throw new Exception(ExcelError::VALUE());
     }
 
     /**
@@ -57,7 +61,7 @@ class ConvertBase
                 return substr(str_pad($value, $places, '0', STR_PAD_LEFT), -10);
             }
 
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return substr($value, -10);

+ 44 - 15
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php

@@ -3,7 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class ConvertBinary extends ConvertBase
 {
@@ -15,17 +15,26 @@ class ConvertBinary extends ConvertBase
      * Excel Function:
      *        BIN2DEC(x)
      *
-     * @param string $value The binary number (as a string) that you want to convert. The number
+     * @param array|string $value The binary number (as a string) that you want to convert. The number
      *                                cannot contain more than 10 characters (10 bits). The most significant
      *                                bit of number is the sign bit. The remaining 9 bits are magnitude bits.
      *                                Negative numbers are represented using two's-complement notation.
      *                                If number is not a valid binary number, or if number contains more than
      *                                10 characters (10 bits), BIN2DEC returns the #NUM! error value.
+     *                      Or can be an array of values
+     *
+     * @return array|string Result, or an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
-    public static function toDecimal($value): string
+    public static function toDecimal($value)
     {
+        if (is_array($value)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+        }
+
         try {
-            $value = self::validateValue(Functions::flattenSingleValue($value));
+            $value = self::validateValue($value);
             $value = self::validateBinary($value);
         } catch (Exception $e) {
             return $e->getMessage();
@@ -49,25 +58,35 @@ class ConvertBinary extends ConvertBase
      * Excel Function:
      *        BIN2HEX(x[,places])
      *
-     * @param string $value The binary number (as a string) that you want to convert. The number
+     * @param array|string $value The binary number (as a string) that you want to convert. The number
      *                                cannot contain more than 10 characters (10 bits). The most significant
      *                                bit of number is the sign bit. The remaining 9 bits are magnitude bits.
      *                                Negative numbers are represented using two's-complement notation.
      *                                If number is not a valid binary number, or if number contains more than
      *                                10 characters (10 bits), BIN2HEX returns the #NUM! error value.
-     * @param int $places The number of characters to use. If places is omitted, BIN2HEX uses the
+     *                      Or can be an array of values
+     * @param array|int $places The number of characters to use. If places is omitted, BIN2HEX uses the
      *                                minimum number of characters necessary. Places is useful for padding the
      *                                return value with leading 0s (zeros).
      *                                If places is not an integer, it is truncated.
      *                                If places is nonnumeric, BIN2HEX returns the #VALUE! error value.
      *                                If places is negative, BIN2HEX returns the #NUM! error value.
+     *                      Or can be an array of values
+     *
+     * @return array|string Result, or an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
-    public static function toHex($value, $places = null): string
+    public static function toHex($value, $places = null)
     {
+        if (is_array($value) || is_array($places)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+        }
+
         try {
-            $value = self::validateValue(Functions::flattenSingleValue($value));
+            $value = self::validateValue($value);
             $value = self::validateBinary($value);
-            $places = self::validatePlaces(Functions::flattenSingleValue($places));
+            $places = self::validatePlaces($places);
         } catch (Exception $e) {
             return $e->getMessage();
         }
@@ -92,25 +111,35 @@ class ConvertBinary extends ConvertBase
      * Excel Function:
      *        BIN2OCT(x[,places])
      *
-     * @param string $value The binary number (as a string) that you want to convert. The number
+     * @param array|string $value The binary number (as a string) that you want to convert. The number
      *                                cannot contain more than 10 characters (10 bits). The most significant
      *                                bit of number is the sign bit. The remaining 9 bits are magnitude bits.
      *                                Negative numbers are represented using two's-complement notation.
      *                                If number is not a valid binary number, or if number contains more than
      *                                10 characters (10 bits), BIN2OCT returns the #NUM! error value.
-     * @param int $places The number of characters to use. If places is omitted, BIN2OCT uses the
+     *                      Or can be an array of values
+     * @param array|int $places The number of characters to use. If places is omitted, BIN2OCT uses the
      *                                minimum number of characters necessary. Places is useful for padding the
      *                                return value with leading 0s (zeros).
      *                                If places is not an integer, it is truncated.
      *                                If places is nonnumeric, BIN2OCT returns the #VALUE! error value.
      *                                If places is negative, BIN2OCT returns the #NUM! error value.
+     *                      Or can be an array of values
+     *
+     * @return array|string Result, or an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
-    public static function toOctal($value, $places = null): string
+    public static function toOctal($value, $places = null)
     {
+        if (is_array($value) || is_array($places)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+        }
+
         try {
-            $value = self::validateValue(Functions::flattenSingleValue($value));
+            $value = self::validateValue($value);
             $value = self::validateBinary($value);
-            $places = self::validatePlaces(Functions::flattenSingleValue($places));
+            $places = self::validatePlaces($places);
         } catch (Exception $e) {
             return $e->getMessage();
         }
@@ -126,7 +155,7 @@ class ConvertBinary extends ConvertBase
     protected static function validateBinary(string $value): string
     {
         if ((strlen($value) > preg_match_all('/[01]/', $value)) || (strlen($value) > 10)) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $value;

+ 50 - 20
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php

@@ -3,7 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class ConvertDecimal extends ConvertBase
 {
@@ -22,7 +22,7 @@ class ConvertDecimal extends ConvertBase
      * Excel Function:
      *        DEC2BIN(x[,places])
      *
-     * @param string $value The decimal integer you want to convert. If number is negative,
+     * @param array|string $value The decimal integer you want to convert. If number is negative,
      *                          valid place values are ignored and DEC2BIN returns a 10-character
      *                          (10-bit) binary number in which the most significant bit is the sign
      *                          bit. The remaining 9 bits are magnitude bits. Negative numbers are
@@ -32,26 +32,36 @@ class ConvertDecimal extends ConvertBase
      *                      If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
      *                      If DEC2BIN requires more than places characters, it returns the #NUM!
      *                          error value.
-     * @param int $places The number of characters to use. If places is omitted, DEC2BIN uses
+     *                      Or can be an array of values
+     * @param array|int $places The number of characters to use. If places is omitted, DEC2BIN uses
      *                          the minimum number of characters necessary. Places is useful for
      *                          padding the return value with leading 0s (zeros).
      *                      If places is not an integer, it is truncated.
      *                      If places is nonnumeric, DEC2BIN returns the #VALUE! error value.
      *                      If places is zero or negative, DEC2BIN returns the #NUM! error value.
+     *                      Or can be an array of values
+     *
+     * @return array|string Result, or an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
-    public static function toBinary($value, $places = null): string
+    public static function toBinary($value, $places = null)
     {
+        if (is_array($value) || is_array($places)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+        }
+
         try {
-            $value = self::validateValue(Functions::flattenSingleValue($value));
+            $value = self::validateValue($value);
             $value = self::validateDecimal($value);
-            $places = self::validatePlaces(Functions::flattenSingleValue($places));
+            $places = self::validatePlaces($places);
         } catch (Exception $e) {
             return $e->getMessage();
         }
 
         $value = (int) floor((float) $value);
         if ($value > self::LARGEST_BINARY_IN_DECIMAL || $value < self::SMALLEST_BINARY_IN_DECIMAL) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         $r = decbin($value);
@@ -69,7 +79,7 @@ class ConvertDecimal extends ConvertBase
      * Excel Function:
      *        DEC2HEX(x[,places])
      *
-     * @param string $value The decimal integer you want to convert. If number is negative,
+     * @param array|string $value The decimal integer you want to convert. If number is negative,
      *                          places is ignored and DEC2HEX returns a 10-character (40-bit)
      *                          hexadecimal number in which the most significant bit is the sign
      *                          bit. The remaining 39 bits are magnitude bits. Negative numbers
@@ -79,26 +89,36 @@ class ConvertDecimal extends ConvertBase
      *                      If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
      *                      If DEC2HEX requires more than places characters, it returns the
      *                          #NUM! error value.
-     * @param int $places The number of characters to use. If places is omitted, DEC2HEX uses
+     *                      Or can be an array of values
+     * @param array|int $places The number of characters to use. If places is omitted, DEC2HEX uses
      *                          the minimum number of characters necessary. Places is useful for
      *                          padding the return value with leading 0s (zeros).
      *                      If places is not an integer, it is truncated.
      *                      If places is nonnumeric, DEC2HEX returns the #VALUE! error value.
      *                      If places is zero or negative, DEC2HEX returns the #NUM! error value.
+     *                      Or can be an array of values
+     *
+     * @return array|string Result, or an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
-    public static function toHex($value, $places = null): string
+    public static function toHex($value, $places = null)
     {
+        if (is_array($value) || is_array($places)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+        }
+
         try {
-            $value = self::validateValue(Functions::flattenSingleValue($value));
+            $value = self::validateValue($value);
             $value = self::validateDecimal($value);
-            $places = self::validatePlaces(Functions::flattenSingleValue($places));
+            $places = self::validatePlaces($places);
         } catch (Exception $e) {
             return $e->getMessage();
         }
 
         $value = floor((float) $value);
         if ($value > self::LARGEST_HEX_IN_DECIMAL || $value < self::SMALLEST_HEX_IN_DECIMAL) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
         $r = strtoupper(dechex((int) $value));
         $r = self::hex32bit($value, $r);
@@ -135,7 +155,7 @@ class ConvertDecimal extends ConvertBase
      * Excel Function:
      *        DEC2OCT(x[,places])
      *
-     * @param string $value The decimal integer you want to convert. If number is negative,
+     * @param array|string $value The decimal integer you want to convert. If number is negative,
      *                          places is ignored and DEC2OCT returns a 10-character (30-bit)
      *                          octal number in which the most significant bit is the sign bit.
      *                          The remaining 29 bits are magnitude bits. Negative numbers are
@@ -145,26 +165,36 @@ class ConvertDecimal extends ConvertBase
      *                      If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
      *                      If DEC2OCT requires more than places characters, it returns the
      *                          #NUM! error value.
-     * @param int $places The number of characters to use. If places is omitted, DEC2OCT uses
+     *                      Or can be an array of values
+     * @param array|int $places The number of characters to use. If places is omitted, DEC2OCT uses
      *                          the minimum number of characters necessary. Places is useful for
      *                          padding the return value with leading 0s (zeros).
      *                      If places is not an integer, it is truncated.
      *                      If places is nonnumeric, DEC2OCT returns the #VALUE! error value.
      *                      If places is zero or negative, DEC2OCT returns the #NUM! error value.
+     *                      Or can be an array of values
+     *
+     * @return array|string Result, or an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
-    public static function toOctal($value, $places = null): string
+    public static function toOctal($value, $places = null)
     {
+        if (is_array($value) || is_array($places)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+        }
+
         try {
-            $value = self::validateValue(Functions::flattenSingleValue($value));
+            $value = self::validateValue($value);
             $value = self::validateDecimal($value);
-            $places = self::validatePlaces(Functions::flattenSingleValue($places));
+            $places = self::validatePlaces($places);
         } catch (Exception $e) {
             return $e->getMessage();
         }
 
         $value = (int) floor((float) $value);
         if ($value > self::LARGEST_OCTAL_IN_DECIMAL || $value < self::SMALLEST_OCTAL_IN_DECIMAL) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
         $r = decoct($value);
         $r = substr($r, -10);
@@ -175,7 +205,7 @@ class ConvertDecimal extends ConvertBase
     protected static function validateDecimal(string $value): string
     {
         if (strlen($value) > preg_match_all('/[-0123456789.]/', $value)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
 
         return $value;

+ 45 - 16
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php

@@ -3,7 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class ConvertHex extends ConvertBase
 {
@@ -15,7 +15,7 @@ class ConvertHex extends ConvertBase
      * Excel Function:
      *        HEX2BIN(x[,places])
      *
-     * @param string $value The hexadecimal number you want to convert.
+     * @param array|string $value The hexadecimal number you want to convert.
      *                      Number cannot contain more than 10 characters.
      *                      The most significant bit of number is the sign bit (40th bit from the right).
      *                      The remaining 9 bits are magnitude bits.
@@ -25,19 +25,29 @@ class ConvertHex extends ConvertBase
      *                          and if number is positive, it cannot be greater than 1FF.
      *                      If number is not a valid hexadecimal number, HEX2BIN returns the #NUM! error value.
      *                      If HEX2BIN requires more than places characters, it returns the #NUM! error value.
-     * @param int $places The number of characters to use. If places is omitted,
+     *                      Or can be an array of values
+     * @param array|int $places The number of characters to use. If places is omitted,
      *                          HEX2BIN uses the minimum number of characters necessary. Places
      *                          is useful for padding the return value with leading 0s (zeros).
      *                      If places is not an integer, it is truncated.
      *                      If places is nonnumeric, HEX2BIN returns the #VALUE! error value.
      *                      If places is negative, HEX2BIN returns the #NUM! error value.
+     *                      Or can be an array of values
+     *
+     * @return array|string Result, or an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
-    public static function toBinary($value, $places = null): string
+    public static function toBinary($value, $places = null)
     {
+        if (is_array($value) || is_array($places)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+        }
+
         try {
-            $value = self::validateValue(Functions::flattenSingleValue($value));
+            $value = self::validateValue($value);
             $value = self::validateHex($value);
-            $places = self::validatePlaces(Functions::flattenSingleValue($places));
+            $places = self::validatePlaces($places);
         } catch (Exception $e) {
             return $e->getMessage();
         }
@@ -55,25 +65,34 @@ class ConvertHex extends ConvertBase
      * Excel Function:
      *        HEX2DEC(x)
      *
-     * @param string $value The hexadecimal number you want to convert. This number cannot
+     * @param array|string $value The hexadecimal number you want to convert. This number cannot
      *                          contain more than 10 characters (40 bits). The most significant
      *                          bit of number is the sign bit. The remaining 39 bits are magnitude
      *                          bits. Negative numbers are represented using two's-complement
      *                          notation.
      *                      If number is not a valid hexadecimal number, HEX2DEC returns the
      *                          #NUM! error value.
+     *                      Or can be an array of values
+     *
+     * @return array|string Result, or an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
-    public static function toDecimal($value): string
+    public static function toDecimal($value)
     {
+        if (is_array($value)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+        }
+
         try {
-            $value = self::validateValue(Functions::flattenSingleValue($value));
+            $value = self::validateValue($value);
             $value = self::validateHex($value);
         } catch (Exception $e) {
             return $e->getMessage();
         }
 
         if (strlen($value) > 10) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         $binX = '';
@@ -99,7 +118,7 @@ class ConvertHex extends ConvertBase
      * Excel Function:
      *        HEX2OCT(x[,places])
      *
-     * @param string $value The hexadecimal number you want to convert. Number cannot
+     * @param array|string $value The hexadecimal number you want to convert. Number cannot
      *                                    contain more than 10 characters. The most significant bit of
      *                                    number is the sign bit. The remaining 39 bits are magnitude
      *                                    bits. Negative numbers are represented using two's-complement
@@ -112,20 +131,30 @@ class ConvertHex extends ConvertBase
      *                                    the #NUM! error value.
      *                                    If HEX2OCT requires more than places characters, it returns
      *                                    the #NUM! error value.
-     * @param int $places The number of characters to use. If places is omitted, HEX2OCT
+     *                      Or can be an array of values
+     * @param array|int $places The number of characters to use. If places is omitted, HEX2OCT
      *                                    uses the minimum number of characters necessary. Places is
      *                                    useful for padding the return value with leading 0s (zeros).
      *                                    If places is not an integer, it is truncated.
      *                                    If places is nonnumeric, HEX2OCT returns the #VALUE! error
      *                                    value.
      *                                    If places is negative, HEX2OCT returns the #NUM! error value.
+     *                      Or can be an array of values
+     *
+     * @return array|string Result, or an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
-    public static function toOctal($value, $places = null): string
+    public static function toOctal($value, $places = null)
     {
+        if (is_array($value) || is_array($places)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+        }
+
         try {
-            $value = self::validateValue(Functions::flattenSingleValue($value));
+            $value = self::validateValue($value);
             $value = self::validateHex($value);
-            $places = self::validatePlaces(Functions::flattenSingleValue($places));
+            $places = self::validatePlaces($places);
         } catch (Exception $e) {
             return $e->getMessage();
         }
@@ -138,7 +167,7 @@ class ConvertHex extends ConvertBase
     protected static function validateHex(string $value): string
     {
         if (strlen($value) > preg_match_all('/[0123456789ABCDEF]/', $value)) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $value;

+ 45 - 16
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php

@@ -3,7 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class ConvertOctal extends ConvertBase
 {
@@ -15,7 +15,7 @@ class ConvertOctal extends ConvertBase
      * Excel Function:
      *        OCT2BIN(x[,places])
      *
-     * @param string $value The octal number you want to convert. Number may not
+     * @param array|string $value The octal number you want to convert. Number may not
      *                          contain more than 10 characters. The most significant
      *                          bit of number is the sign bit. The remaining 29 bits
      *                          are magnitude bits. Negative numbers are represented
@@ -28,7 +28,8 @@ class ConvertOctal extends ConvertBase
      *                          the #NUM! error value.
      *                      If OCT2BIN requires more than places characters, it
      *                          returns the #NUM! error value.
-     * @param int $places The number of characters to use. If places is omitted,
+     *                      Or can be an array of values
+     * @param array|int $places The number of characters to use. If places is omitted,
      *                          OCT2BIN uses the minimum number of characters necessary.
      *                          Places is useful for padding the return value with
      *                          leading 0s (zeros).
@@ -37,13 +38,22 @@ class ConvertOctal extends ConvertBase
      *                          error value.
      *                      If places is negative, OCT2BIN returns the #NUM! error
      *                          value.
+     *                      Or can be an array of values
+     *
+     * @return array|string Result, or an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
-    public static function toBinary($value, $places = null): string
+    public static function toBinary($value, $places = null)
     {
+        if (is_array($value) || is_array($places)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+        }
+
         try {
-            $value = self::validateValue(Functions::flattenSingleValue($value));
+            $value = self::validateValue($value);
             $value = self::validateOctal($value);
-            $places = self::validatePlaces(Functions::flattenSingleValue($places));
+            $places = self::validatePlaces($places);
         } catch (Exception $e) {
             return $e->getMessage();
         }
@@ -59,18 +69,27 @@ class ConvertOctal extends ConvertBase
      * Excel Function:
      *        OCT2DEC(x)
      *
-     * @param string $value The octal number you want to convert. Number may not contain
+     * @param array|string $value The octal number you want to convert. Number may not contain
      *                          more than 10 octal characters (30 bits). The most significant
      *                          bit of number is the sign bit. The remaining 29 bits are
      *                          magnitude bits. Negative numbers are represented using
      *                          two's-complement notation.
      *                      If number is not a valid octal number, OCT2DEC returns the
      *                          #NUM! error value.
+     *                      Or can be an array of values
+     *
+     * @return array|string Result, or an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
-    public static function toDecimal($value): string
+    public static function toDecimal($value)
     {
+        if (is_array($value)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+        }
+
         try {
-            $value = self::validateValue(Functions::flattenSingleValue($value));
+            $value = self::validateValue($value);
             $value = self::validateOctal($value);
         } catch (Exception $e) {
             return $e->getMessage();
@@ -99,7 +118,7 @@ class ConvertOctal extends ConvertBase
      * Excel Function:
      *        OCT2HEX(x[,places])
      *
-     * @param string $value The octal number you want to convert. Number may not contain
+     * @param array|string $value The octal number you want to convert. Number may not contain
      *                          more than 10 octal characters (30 bits). The most significant
      *                          bit of number is the sign bit. The remaining 29 bits are
      *                          magnitude bits. Negative numbers are represented using
@@ -110,25 +129,35 @@ class ConvertOctal extends ConvertBase
      *                          #NUM! error value.
      *                      If OCT2HEX requires more than places characters, it returns
      *                          the #NUM! error value.
-     * @param int $places The number of characters to use. If places is omitted, OCT2HEX
+     *                      Or can be an array of values
+     * @param array|int $places The number of characters to use. If places is omitted, OCT2HEX
      *                          uses the minimum number of characters necessary. Places is useful
      *                          for padding the return value with leading 0s (zeros).
      *                      If places is not an integer, it is truncated.
      *                      If places is nonnumeric, OCT2HEX returns the #VALUE! error value.
      *                      If places is negative, OCT2HEX returns the #NUM! error value.
+     *                      Or can be an array of values
+     *
+     * @return array|string Result, or an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
-    public static function toHex($value, $places = null): string
+    public static function toHex($value, $places = null)
     {
+        if (is_array($value) || is_array($places)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
+        }
+
         try {
-            $value = self::validateValue(Functions::flattenSingleValue($value));
+            $value = self::validateValue($value);
             $value = self::validateOctal($value);
-            $places = self::validatePlaces(Functions::flattenSingleValue($places));
+            $places = self::validatePlaces($places);
         } catch (Exception $e) {
             return $e->getMessage();
         }
 
         $hexVal = strtoupper(dechex((int) self::toDecimal($value)));
-        $hexVal = (PHP_INT_SIZE === 4 && strlen($value) === 10 && $value[0] >= '4') ? "FF$hexVal" : $hexVal;
+        $hexVal = (PHP_INT_SIZE === 4 && strlen($value) === 10 && $value[0] >= '4') ? "FF{$hexVal}" : $hexVal;
 
         return self::nbrConversionFormat($hexVal, $places);
     }
@@ -137,7 +166,7 @@ class ConvertOctal extends ConvertBase
     {
         $numDigits = (int) preg_match_all('/[01234567]/', $value);
         if (strlen($value) > $numDigits || $numDigits > 10) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $value;

+ 21 - 11
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php

@@ -2,11 +2,14 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class ConvertUOM
 {
+    use ArrayEnabled;
+
     public const CATEGORY_WEIGHT_AND_MASS = 'Weight and Mass';
     public const CATEGORY_DISTANCE = 'Distance';
     public const CATEGORY_TIME = 'Time';
@@ -103,6 +106,7 @@ class ConvertUOM
         'W' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Watt', 'AllowPrefix' => true],
         'w' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Watt', 'AllowPrefix' => true],
         'PS' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Pferdestärke', 'AllowPrefix' => false],
+        // Magnetism
         'T' => ['Group' => self::CATEGORY_MAGNETISM, 'Unit Name' => 'Tesla', 'AllowPrefix' => true],
         'ga' => ['Group' => self::CATEGORY_MAGNETISM, 'Unit Name' => 'Gauss', 'AllowPrefix' => true],
         // Temperature
@@ -518,33 +522,39 @@ class ConvertUOM
      *    Excel Function:
      *        CONVERT(value,fromUOM,toUOM)
      *
-     * @param float|int $value the value in fromUOM to convert
-     * @param string $fromUOM the units for value
-     * @param string $toUOM the units for the result
+     * @param array|float|int|string $value the value in fromUOM to convert
+     *                      Or can be an array of values
+     * @param array|string $fromUOM the units for value
+     *                      Or can be an array of values
+     * @param array|string $toUOM the units for the result
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string Result, or a string containing an error
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function CONVERT($value, $fromUOM, $toUOM)
     {
-        $value = Functions::flattenSingleValue($value);
-        $fromUOM = Functions::flattenSingleValue($fromUOM);
-        $toUOM = Functions::flattenSingleValue($toUOM);
+        if (is_array($value) || is_array($fromUOM) || is_array($toUOM)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $fromUOM, $toUOM);
+        }
 
         if (!is_numeric($value)) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
 
         try {
             [$fromUOM, $fromCategory, $fromMultiplier] = self::getUOMDetails($fromUOM);
             [$toUOM, $toCategory, $toMultiplier] = self::getUOMDetails($toUOM);
         } catch (Exception $e) {
-            return Functions::NA();
+            return ExcelError::NA();
         }
 
         if ($fromCategory !== $toCategory) {
-            return Functions::NA();
+            return ExcelError::NA();
         }
 
+        // @var float $value
         $value *= $fromMultiplier;
 
         if (($fromUOM === $toUOM) && ($fromMultiplier === $toMultiplier)) {

+ 3 - 3
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php

@@ -3,7 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class EngineeringValidations
 {
@@ -13,7 +13,7 @@ class EngineeringValidations
     public static function validateFloat($value): float
     {
         if (!is_numeric($value)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
 
         return (float) $value;
@@ -25,7 +25,7 @@ class EngineeringValidations
     public static function validateInt($value): int
     {
         if (!is_numeric($value)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
 
         return (int) floor((float) $value);

+ 20 - 6
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Erf.php

@@ -2,10 +2,14 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Erf
 {
+    use ArrayEnabled;
+
     private static $twoSqrtPi = 1.128379167095512574;
 
     /**
@@ -22,15 +26,20 @@ class Erf
      *        ERF(lower[,upper])
      *
      * @param mixed $lower Lower bound float for integrating ERF
+     *                      Or can be an array of values
      * @param mixed $upper Upper bound float for integrating ERF.
      *                           If omitted, ERF integrates between zero and lower_limit
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function ERF($lower, $upper = null)
     {
-        $lower = Functions::flattenSingleValue($lower);
-        $upper = Functions::flattenSingleValue($upper);
+        if (is_array($lower) || is_array($upper)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $lower, $upper);
+        }
 
         if (is_numeric($lower)) {
             if ($upper === null) {
@@ -41,7 +50,7 @@ class Erf
             }
         }
 
-        return Functions::VALUE();
+        return ExcelError::VALUE();
     }
 
     /**
@@ -53,12 +62,17 @@ class Erf
      *        ERF.PRECISE(limit)
      *
      * @param mixed $limit Float bound for integrating ERF, other bound is zero
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function ERFPRECISE($limit)
     {
-        $limit = Functions::flattenSingleValue($limit);
+        if (is_array($limit)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $limit);
+        }
 
         return self::ERF($limit);
     }

+ 13 - 4
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php

@@ -2,10 +2,14 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class ErfC
 {
+    use ArrayEnabled;
+
     /**
      * ERFC.
      *
@@ -20,18 +24,23 @@ class ErfC
      *        ERFC(x)
      *
      * @param mixed $value The float lower bound for integrating ERFC
+     *                      Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function ERFC($value)
     {
-        $value = Functions::flattenSingleValue($value);
+        if (is_array($value)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+        }
 
         if (is_numeric($value)) {
             return self::erfcValue($value);
         }
 
-        return Functions::VALUE();
+        return ExcelError::VALUE();
     }
 
     //
@@ -45,7 +54,7 @@ class ErfC
             return 1 - Erf::erfValue($value);
         }
         if ($value < 0) {
-            return 2 - self::ERFC(-$value);
+            return 2 - self::erfcValue(-$value);
         }
         $a = $n = 1;
         $b = $c = $value;

+ 3 - 1
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ExceptionHandler.php

@@ -9,7 +9,9 @@ class ExceptionHandler
      */
     public function __construct()
     {
-        set_error_handler([Exception::class, 'errorHandlerCallback'], E_ALL);
+        /** @var callable */
+        $callable = [Exception::class, 'errorHandlerCallback'];
+        set_error_handler($callable, E_ALL);
     }
 
     /**

+ 6 - 6
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php

@@ -587,10 +587,10 @@ class Financial
      * @see Financial\Dollar::decimal()
      *      Use the decimal() method in the Financial\Dollar class instead
      *
-     * @param float $fractional_dollar Fractional Dollar
-     * @param int $fraction Fraction
+     * @param array|float $fractional_dollar Fractional Dollar
+     * @param array|int $fraction Fraction
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function DOLLARDE($fractional_dollar = null, $fraction = 0)
     {
@@ -612,10 +612,10 @@ class Financial
      * @see Financial\Dollar::fractional()
      *      Use the fractional() method in the Financial\Dollar class instead
      *
-     * @param float $decimal_dollar Decimal Dollar
-     * @param int $fraction Fraction
+     * @param array|float $decimal_dollar Decimal Dollar
+     * @param array|int $fraction Fraction
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function DOLLARFR($decimal_dollar = null, $fraction = 0)
     {

+ 11 - 7
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Amortization.php

@@ -70,10 +70,12 @@ class Amortization
             return $e->getMessage();
         }
 
-        $yearFrac = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
-        if (is_string($yearFrac)) {
-            return $yearFrac;
+        $yearFracx = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
+        if (is_string($yearFracx)) {
+            return $yearFracx;
         }
+        /** @var float */
+        $yearFrac = $yearFracx;
 
         $amortiseCoeff = self::getAmortizationCoefficient($rate);
 
@@ -161,14 +163,16 @@ class Amortization
         $fCostDelta = $cost - $salvage;
         //    Note, quirky variation for leap years on the YEARFRAC for this function
         $purchasedYear = DateTimeExcel\DateParts::year($purchased);
-        $yearFrac = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
-        if (is_string($yearFrac)) {
-            return $yearFrac;
+        $yearFracx = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
+        if (is_string($yearFracx)) {
+            return $yearFracx;
         }
+        /** @var float */
+        $yearFrac = $yearFracx;
 
         if (
             ($basis == FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL) &&
-            ($yearFrac < 1) && (DateTimeExcel\Helpers::isLeapYear($purchasedYear))
+            ($yearFrac < 1) && (Functions::scalar(DateTimeExcel\Helpers::isLeapYear($purchasedYear)))
         ) {
             $yearFrac *= 365 / 366;
         }

+ 2 - 2
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php

@@ -5,7 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\CashFlow;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\FinancialValidations;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class CashFlowValidations extends FinancialValidations
 {
@@ -29,7 +29,7 @@ class CashFlowValidations extends FinancialValidations
             $type !== FinancialConstants::PAYMENT_END_OF_PERIOD &&
             $type !== FinancialConstants::PAYMENT_BEGINNING_OF_PERIOD
         ) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $rate;

+ 4 - 3
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic.php

@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\CashFlow\CashFlowValidations;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Periodic
 {
@@ -94,7 +95,7 @@ class Periodic
 
         // Validate parameters
         if ($numberOfPeriods < 0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return self::calculatePresentValue($rate, $numberOfPeriods, $payment, $futureValue, $type);
@@ -138,7 +139,7 @@ class Periodic
 
         // Validate parameters
         if ($payment == 0.0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return self::calculatePeriods($rate, $payment, $presentValue, $futureValue, $type);
@@ -187,7 +188,7 @@ class Periodic
     ) {
         if ($rate != 0.0) {
             if ($presentValue == 0.0) {
-                return Functions::NAN();
+                return ExcelError::NAN();
             }
 
             return log(($payment * (1 + $rate * $type) / $rate - $futureValue) /

+ 3 - 2
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php

@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\CashFlow\CashFlowValidations;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Cumulative
 {
@@ -57,7 +58,7 @@ class Cumulative
 
         // Validate parameters
         if ($start < 1 || $start > $end) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         // Calculate
@@ -122,7 +123,7 @@ class Cumulative
 
         // Validate parameters
         if ($start < 1 || $start > $end) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
 
         // Calculate

+ 6 - 5
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php

@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\CashFlow\CashFlowValidations;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Interest
 {
@@ -59,7 +60,7 @@ class Interest
 
         // Validate parameters
         if ($period <= 0 || $period > $numberOfPeriods) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         // Calculate
@@ -106,7 +107,7 @@ class Interest
 
         // Validate parameters
         if ($period <= 0 || $period > $numberOfPeriods) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         // Return value
@@ -193,13 +194,13 @@ class Interest
             $rate = $rate1;
         }
 
-        return $close ? $rate : Functions::NAN();
+        return $close ? $rate : ExcelError::NAN();
     }
 
     private static function rateNextGuess($rate, $numberOfPeriods, $payment, $presentValue, $futureValue, $type)
     {
         if ($rate == 0.0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
         $tt1 = ($rate + 1) ** $numberOfPeriods;
         $tt2 = ($rate + 1) ** ($numberOfPeriods - 1);
@@ -208,7 +209,7 @@ class Interest
             * ($rate * $type + 1) / ($rate * $rate) + $numberOfPeriods
             * $payment * $tt2 * ($rate * $type + 1) / $rate + $payment * ($tt1 - 1) * $type / $rate;
         if ($denominator == 0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return $numerator / $denominator;

+ 2 - 1
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php

@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\CashFlow\CashFlowValidations;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Payments
 {
@@ -97,7 +98,7 @@ class Payments
 
         // Validate parameters
         if ($period <= 0 || $period > $numberOfPeriods) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         // Calculate

+ 3 - 2
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php

@@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\CashFlow;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Single
 {
@@ -67,7 +68,7 @@ class Single
 
         // Validate parameters
         if ($rate <= 0.0 || $presentValue <= 0.0 || $futureValue <= 0.0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (log($futureValue) - log($presentValue)) / log(1 + $rate);
@@ -100,7 +101,7 @@ class Single
 
         // Validate parameters
         if ($periods <= 0.0 || $presentValue <= 0.0 || $futureValue < 0.0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return ($futureValue / $presentValue) ** (1 / $periods) - 1;

+ 36 - 21
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php

@@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\CashFlow\Variable;
 use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class NonPeriodic
 {
@@ -12,6 +13,8 @@ class NonPeriodic
 
     const FINANCIAL_PRECISION = 1.0e-08;
 
+    const DEFAULT_GUESS = 0.1;
+
     /**
      * XIRR.
      *
@@ -25,11 +28,11 @@ class NonPeriodic
      * @param mixed[] $dates      A series of payment dates
      *                                The first payment date indicates the beginning of the schedule of payments
      *                                All other dates must be later than this date, but they may occur in any order
-     * @param float $guess        An optional guess at the expected answer
+     * @param mixed $guess        An optional guess at the expected answer
      *
      * @return float|string
      */
-    public static function rate($values, $dates, $guess = 0.1)
+    public static function rate($values, $dates, $guess = self::DEFAULT_GUESS)
     {
         $rslt = self::xirrPart1($values, $dates);
         if ($rslt !== '') {
@@ -37,9 +40,13 @@ class NonPeriodic
         }
 
         // create an initial range, with a root somewhere between 0 and guess
-        $guess = Functions::flattenSingleValue($guess);
+        $guess = Functions::flattenSingleValue($guess) ?? self::DEFAULT_GUESS;
+        if (!is_numeric($guess)) {
+            return ExcelError::VALUE();
+        }
+        $guess = ($guess + 0.0) ?: self::DEFAULT_GUESS;
         $x1 = 0.0;
-        $x2 = $guess ?: 0.1;
+        $x2 = $guess + 0.0;
         $f1 = self::xnpvOrdered($x1, $values, $dates, false);
         $f2 = self::xnpvOrdered($x2, $values, $dates, false);
         $found = false;
@@ -54,13 +61,15 @@ class NonPeriodic
 
                 break;
             } elseif (abs($f1) < abs($f2)) {
-                $f1 = self::xnpvOrdered($x1 += 1.6 * ($x1 - $x2), $values, $dates, false);
+                $x1 += 1.6 * ($x1 - $x2);
+                $f1 = self::xnpvOrdered($x1, $values, $dates, false);
             } else {
-                $f2 = self::xnpvOrdered($x2 += 1.6 * ($x2 - $x1), $values, $dates, false);
+                $x2 += 1.6 * ($x2 - $x1);
+                $f2 = self::xnpvOrdered($x2, $values, $dates, false);
             }
         }
         if (!$found) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return self::xirrPart3($values, $dates, $x1, $x2);
@@ -104,13 +113,15 @@ class NonPeriodic
      */
     private static function xirrPart1(&$values, &$dates): string
     {
-        if (!is_array($values) && !is_array($dates)) {
-            return Functions::NA();
-        }
         $values = Functions::flattenArray($values);
         $dates = Functions::flattenArray($dates);
+        $valuesIsArray = count($values) > 1;
+        $datesIsArray = count($dates) > 1;
+        if (!$valuesIsArray && !$datesIsArray) {
+            return ExcelError::NA();
+        }
         if (count($values) != count($dates)) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         $datesCount = count($dates);
@@ -133,7 +144,7 @@ class NonPeriodic
         for ($i = 0; $i < $valCount; ++$i) {
             $fld = $values[$i];
             if (!is_numeric($fld)) {
-                return Functions::VALUE();
+                return ExcelError::VALUE();
             } elseif ($fld > 0) {
                 $foundpos = true;
             } elseif ($fld < 0) {
@@ -141,7 +152,7 @@ class NonPeriodic
             }
         }
         if (!self::bothNegAndPos($foundneg, $foundpos)) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return '';
@@ -161,7 +172,7 @@ class NonPeriodic
             $dx = $x1 - $x2;
         }
 
-        $rslt = Functions::VALUE();
+        $rslt = ExcelError::VALUE();
         for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
             $dx *= 0.5;
             $x_mid = $rtb + $dx;
@@ -203,7 +214,7 @@ class NonPeriodic
         $xnpv = 0.0;
         for ($i = 0; $i < $valCount; ++$i) {
             if (!is_numeric($values[$i])) {
-                return Functions::VALUE();
+                return ExcelError::VALUE();
             }
 
             try {
@@ -212,17 +223,21 @@ class NonPeriodic
                 return $e->getMessage();
             }
             if ($date0 > $datei) {
-                $dif = $ordered ? Functions::NAN() : -((int) DateTimeExcel\Difference::interval($datei, $date0, 'd'));
+                $dif = $ordered ? ExcelError::NAN() : -((int) DateTimeExcel\Difference::interval($datei, $date0, 'd'));
             } else {
                 $dif = DateTimeExcel\Difference::interval($date0, $datei, 'd');
             }
             if (!is_numeric($dif)) {
                 return $dif;
             }
-            $xnpv += $values[$i] / (1 + $rate) ** ($dif / 365);
+            if ($rate <= -1.0) {
+                $xnpv += -abs($values[$i]) / (-1 - $rate) ** ($dif / 365);
+            } else {
+                $xnpv += $values[$i] / (1 + $rate) ** ($dif / 365);
+            }
         }
 
-        return is_finite($xnpv) ? $xnpv : Functions::VALUE();
+        return is_finite($xnpv) ? $xnpv : ExcelError::VALUE();
     }
 
     /**
@@ -231,14 +246,14 @@ class NonPeriodic
     private static function validateXnpv($rate, array $values, array $dates): void
     {
         if (!is_numeric($rate)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
         $valCount = count($values);
         if ($valCount != count($dates)) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
         if ($valCount > 1 && ((min($values) > 0) || (max($values) < 0))) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
     }
 }

+ 7 - 6
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php

@@ -3,6 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\CashFlow\Variable;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Periodic
 {
@@ -33,7 +34,7 @@ class Periodic
     public static function rate($values, $guess = 0.1)
     {
         if (!is_array($values)) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
         $values = Functions::flattenArray($values);
         $guess = Functions::flattenSingleValue($guess);
@@ -54,7 +55,7 @@ class Periodic
             }
         }
         if (($f1 * $f2) > 0.0) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
 
         $f = self::presentValue($x1, $values);
@@ -78,7 +79,7 @@ class Periodic
             }
         }
 
-        return Functions::VALUE();
+        return ExcelError::VALUE();
     }
 
     /**
@@ -101,7 +102,7 @@ class Periodic
     public static function modifiedRate($values, $financeRate, $reinvestmentRate)
     {
         if (!is_array($values)) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
         $values = Functions::flattenArray($values);
         $financeRate = Functions::flattenSingleValue($financeRate);
@@ -121,13 +122,13 @@ class Periodic
         }
 
         if (($npvNeg === 0.0) || ($npvPos === 0.0) || ($reinvestmentRate <= -1.0)) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
 
         $mirr = ((-$npvPos * $rr ** $n)
                 / ($npvNeg * ($rr))) ** (1.0 / ($n - 1)) - 1.0;
 
-        return is_finite($mirr) ? $mirr : Functions::VALUE();
+        return is_finite($mirr) ? $mirr : ExcelError::VALUE();
     }
 
     /**

+ 6 - 4
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Coupons.php

@@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Shared\Date;
 
 class Coupons
@@ -64,9 +65,9 @@ class Coupons
             return $e->getMessage();
         }
 
-        $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
+        $daysPerYear = Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
         if (is_string($daysPerYear)) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
         $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS);
 
@@ -134,7 +135,7 @@ class Coupons
             case FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL:
                 // Actual/actual
                 if ($frequency == FinancialConstants::FREQUENCY_ANNUAL) {
-                    $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
+                    $daysPerYear = (int) Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
 
                     return $daysPerYear / $frequency;
                 }
@@ -198,6 +199,7 @@ class Coupons
             return $e->getMessage();
         }
 
+        /** @var int */
         $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
         $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);
 
@@ -409,7 +411,7 @@ class Coupons
     private static function validateCouponPeriod(float $settlement, float $maturity): void
     {
         if ($settlement >= $maturity) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
     }
 }

+ 10 - 9
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php

@@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Depreciation
 {
@@ -117,7 +118,7 @@ class Depreciation
         }
 
         if ($period > $life) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         // Loop through each period calculating the depreciation
@@ -161,7 +162,7 @@ class Depreciation
         }
 
         if ($life === 0.0) {
-            return Functions::DIV0();
+            return ExcelError::DIV0();
         }
 
         return ($cost - $salvage) / $life;
@@ -196,7 +197,7 @@ class Depreciation
         }
 
         if ($period > $life) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         $syd = (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
@@ -208,7 +209,7 @@ class Depreciation
     {
         $cost = FinancialValidations::validateFloat($cost);
         if ($cost < 0.0 && $negativeValueAllowed === false) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $cost;
@@ -218,7 +219,7 @@ class Depreciation
     {
         $salvage = FinancialValidations::validateFloat($salvage);
         if ($salvage < 0.0 && $negativeValueAllowed === false) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $salvage;
@@ -228,7 +229,7 @@ class Depreciation
     {
         $life = FinancialValidations::validateFloat($life);
         if ($life < 0.0 && $negativeValueAllowed === false) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $life;
@@ -238,7 +239,7 @@ class Depreciation
     {
         $period = FinancialValidations::validateFloat($period);
         if ($period <= 0.0 && $negativeValueAllowed === false) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $period;
@@ -248,7 +249,7 @@ class Depreciation
     {
         $month = FinancialValidations::validateInt($month);
         if ($month < 1) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $month;
@@ -258,7 +259,7 @@ class Depreciation
     {
         $factor = FinancialValidations::validateFloat($factor);
         if ($factor <= 0.0) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $factor;

+ 54 - 19
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Dollar.php

@@ -2,23 +2,34 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
+use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Calculation\TextData\Format;
 
 class Dollar
 {
+    use ArrayEnabled;
+
     /**
      * DOLLAR.
      *
      * This function converts a number to text using currency format, with the decimals rounded to the specified place.
      * The format used is $#,##0.00_);($#,##0.00)..
      *
-     * @param mixed $number The value to format
+     * @param mixed $number The value to format, or can be an array of numbers
+     *                         Or can be an array of values
      * @param mixed $precision The number of digits to display to the right of the decimal point (as an integer).
      *                            If precision is negative, number is rounded to the left of the decimal point.
      *                            If you omit precision, it is assumed to be 2
+     *              Or can be an array of precision values
+     *
+     * @return array|string
+     *         If an array of values is passed for either of the arguments, then the returned result
+     *            will also be an array with matching dimensions
      */
-    public static function format($number, $precision = 2): string
+    public static function format($number, $precision = 2)
     {
         return Format::DOLLAR($number, $precision);
     }
@@ -34,25 +45,37 @@ class Dollar
      *        DOLLARDE(fractional_dollar,fraction)
      *
      * @param mixed $fractionalDollar Fractional Dollar
+     *              Or can be an array of values
      * @param mixed $fraction Fraction
+     *              Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function decimal($fractionalDollar = null, $fraction = 0)
     {
-        $fractionalDollar = Functions::flattenSingleValue($fractionalDollar);
-        $fraction = (int) Functions::flattenSingleValue($fraction);
+        if (is_array($fractionalDollar) || is_array($fraction)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $fractionalDollar, $fraction);
+        }
+
+        try {
+            $fractionalDollar = FinancialValidations::validateFloat(
+                Functions::flattenSingleValue($fractionalDollar) ?? 0.0
+            );
+            $fraction = FinancialValidations::validateInt(Functions::flattenSingleValue($fraction));
+        } catch (Exception $e) {
+            return $e->getMessage();
+        }
 
-        // Validate parameters
-        if ($fractionalDollar === null || $fraction < 0) {
-            return Functions::NAN();
+        // Additional parameter validations
+        if ($fraction < 0) {
+            return ExcelError::NAN();
         }
         if ($fraction == 0) {
-            return Functions::DIV0();
+            return ExcelError::DIV0();
         }
 
-        $dollars = floor($fractionalDollar);
-        $cents = fmod($fractionalDollar, 1);
+        $dollars = ($fractionalDollar < 0) ? ceil($fractionalDollar) : floor($fractionalDollar);
+        $cents = fmod($fractionalDollar, 1.0);
         $cents /= $fraction;
         $cents *= 10 ** ceil(log10($fraction));
 
@@ -70,24 +93,36 @@ class Dollar
      *        DOLLARFR(decimal_dollar,fraction)
      *
      * @param mixed $decimalDollar Decimal Dollar
+     *              Or can be an array of values
      * @param mixed $fraction Fraction
+     *              Or can be an array of values
      *
-     * @return float|string
+     * @return array|float|string
      */
     public static function fractional($decimalDollar = null, $fraction = 0)
     {
-        $decimalDollar = Functions::flattenSingleValue($decimalDollar);
-        $fraction = (int) Functions::flattenSingleValue($fraction);
+        if (is_array($decimalDollar) || is_array($fraction)) {
+            return self::evaluateArrayArguments([self::class, __FUNCTION__], $decimalDollar, $fraction);
+        }
+
+        try {
+            $decimalDollar = FinancialValidations::validateFloat(
+                Functions::flattenSingleValue($decimalDollar) ?? 0.0
+            );
+            $fraction = FinancialValidations::validateInt(Functions::flattenSingleValue($fraction));
+        } catch (Exception $e) {
+            return $e->getMessage();
+        }
 
-        // Validate parameters
-        if ($decimalDollar === null || $fraction < 0) {
-            return Functions::NAN();
+        // Additional parameter validations
+        if ($fraction < 0) {
+            return ExcelError::NAN();
         }
         if ($fraction == 0) {
-            return Functions::DIV0();
+            return ExcelError::DIV0();
         }
 
-        $dollars = floor($decimalDollar);
+        $dollars = ($decimalDollar < 0.0) ? ceil($decimalDollar) : floor($decimalDollar);
         $cents = fmod($decimalDollar, 1);
         $cents *= $fraction;
         $cents *= 10 ** (-ceil(log10($fraction)));

+ 11 - 11
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php

@@ -5,7 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
 use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class FinancialValidations
 {
@@ -39,7 +39,7 @@ class FinancialValidations
     public static function validateFloat($value): float
     {
         if (!is_numeric($value)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
 
         return (float) $value;
@@ -51,7 +51,7 @@ class FinancialValidations
     public static function validateInt($value): int
     {
         if (!is_numeric($value)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
 
         return (int) floor((float) $value);
@@ -64,7 +64,7 @@ class FinancialValidations
     {
         $rate = self::validateFloat($rate);
         if ($rate < 0.0) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $rate;
@@ -81,7 +81,7 @@ class FinancialValidations
             ($frequency !== FinancialConstants::FREQUENCY_SEMI_ANNUAL) &&
             ($frequency !== FinancialConstants::FREQUENCY_QUARTERLY)
         ) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $frequency;
@@ -93,12 +93,12 @@ class FinancialValidations
     public static function validateBasis($basis): int
     {
         if (!is_numeric($basis)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
 
         $basis = (int) $basis;
         if (($basis < 0) || ($basis > 4)) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $basis;
@@ -111,7 +111,7 @@ class FinancialValidations
     {
         $price = self::validateFloat($price);
         if ($price < 0.0) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $price;
@@ -124,7 +124,7 @@ class FinancialValidations
     {
         $parValue = self::validateFloat($parValue);
         if ($parValue < 0.0) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $parValue;
@@ -137,7 +137,7 @@ class FinancialValidations
     {
         $yield = self::validateFloat($yield);
         if ($yield < 0.0) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $yield;
@@ -150,7 +150,7 @@ class FinancialValidations
     {
         $discount = self::validateFloat($discount);
         if ($discount <= 0.0) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $discount;

+ 3 - 3
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Helpers.php

@@ -5,7 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
 use DateTimeInterface;
 use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Helpers
 {
@@ -27,7 +27,7 @@ class Helpers
     public static function daysPerYear($year, $basis = 0)
     {
         if (!is_numeric($basis)) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         switch ($basis) {
@@ -41,7 +41,7 @@ class Helpers
                 return (DateTimeExcel\Helpers::isLeapYear($year)) ? 366 : 365;
         }
 
-        return Functions::NAN();
+        return ExcelError::NAN();
     }
 
     /**

+ 3 - 2
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php

@@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class InterestRate
 {
@@ -34,7 +35,7 @@ class InterestRate
         }
 
         if ($nominalRate <= 0 || $periodsPerYear < 1) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return ((1 + $nominalRate / $periodsPerYear) ** $periodsPerYear) - 1;
@@ -63,7 +64,7 @@ class InterestRate
         }
 
         if ($effectiveRate <= 0 || $periodsPerYear < 1) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         // Calculate

+ 3 - 3
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php

@@ -78,12 +78,12 @@ class AccruedInterest
             return $e->getMessage();
         }
 
-        $daysBetweenIssueAndSettlement = YearFrac::fraction($issue, $settlement, $basis);
+        $daysBetweenIssueAndSettlement = Functions::scalar(YearFrac::fraction($issue, $settlement, $basis));
         if (!is_numeric($daysBetweenIssueAndSettlement)) {
             //    return date error
             return $daysBetweenIssueAndSettlement;
         }
-        $daysBetweenFirstInterestAndSettlement = YearFrac::fraction($firstInterest, $settlement, $basis);
+        $daysBetweenFirstInterestAndSettlement = Functions::scalar(YearFrac::fraction($firstInterest, $settlement, $basis));
         if (!is_numeric($daysBetweenFirstInterestAndSettlement)) {
             //    return date error
             return $daysBetweenFirstInterestAndSettlement;
@@ -140,7 +140,7 @@ class AccruedInterest
             return $e->getMessage();
         }
 
-        $daysBetweenIssueAndSettlement = YearFrac::fraction($issue, $settlement, $basis);
+        $daysBetweenIssueAndSettlement = Functions::scalar(YearFrac::fraction($issue, $settlement, $basis));
         if (!is_numeric($daysBetweenIssueAndSettlement)) {
             //    return date error
             return $daysBetweenIssueAndSettlement;

+ 7 - 6
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php

@@ -8,6 +8,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstan
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\Coupons;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Price
 {
@@ -134,7 +135,7 @@ class Price
             return $e->getMessage();
         }
 
-        $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
+        $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
         if (!is_numeric($daysBetweenSettlementAndMaturity)) {
             //    return date error
             return $daysBetweenSettlementAndMaturity;
@@ -194,23 +195,23 @@ class Price
             return $e->getMessage();
         }
 
-        $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
+        $daysPerYear = Functions::scalar(Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis));
         if (!is_numeric($daysPerYear)) {
             return $daysPerYear;
         }
-        $daysBetweenIssueAndSettlement = DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis);
+        $daysBetweenIssueAndSettlement = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis));
         if (!is_numeric($daysBetweenIssueAndSettlement)) {
             //    return date error
             return $daysBetweenIssueAndSettlement;
         }
         $daysBetweenIssueAndSettlement *= $daysPerYear;
-        $daysBetweenIssueAndMaturity = DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis);
+        $daysBetweenIssueAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis));
         if (!is_numeric($daysBetweenIssueAndMaturity)) {
             //    return date error
             return $daysBetweenIssueAndMaturity;
         }
         $daysBetweenIssueAndMaturity *= $daysPerYear;
-        $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
+        $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
         if (!is_numeric($daysBetweenSettlementAndMaturity)) {
             //    return date error
             return $daysBetweenSettlementAndMaturity;
@@ -270,7 +271,7 @@ class Price
         }
 
         if ($investment <= 0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
         $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
         if (!is_numeric($daysBetweenSettlementAndMaturity)) {

+ 5 - 4
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php

@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Rates
 {
@@ -60,10 +61,10 @@ class Rates
         }
 
         if ($price <= 0.0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
-        $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
+        $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
         if (!is_numeric($daysBetweenSettlementAndMaturity)) {
             //    return date error
             return $daysBetweenSettlementAndMaturity;
@@ -123,10 +124,10 @@ class Rates
         }
 
         if ($investment <= 0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
-        $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
+        $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
         if (!is_numeric($daysBetweenSettlementAndMaturity)) {
             //    return date error
             return $daysBetweenSettlementAndMaturity;

+ 3 - 3
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php

@@ -4,7 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\FinancialValidations;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class SecurityValidations extends FinancialValidations
 {
@@ -23,7 +23,7 @@ class SecurityValidations extends FinancialValidations
     public static function validateSecurityPeriod($settlement, $maturity): void
     {
         if ($settlement >= $maturity) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
     }
 
@@ -34,7 +34,7 @@ class SecurityValidations extends FinancialValidations
     {
         $redemption = self::validateFloat($redemption);
         if ($redemption <= 0.0) {
-            throw new Exception(Functions::NAN());
+            throw new Exception(ExcelError::NAN());
         }
 
         return $redemption;

+ 4 - 4
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php

@@ -61,7 +61,7 @@ class Yields
         if (!is_numeric($daysPerYear)) {
             return $daysPerYear;
         }
-        $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
+        $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
         if (!is_numeric($daysBetweenSettlementAndMaturity)) {
             //    return date error
             return $daysBetweenSettlementAndMaturity;
@@ -126,19 +126,19 @@ class Yields
         if (!is_numeric($daysPerYear)) {
             return $daysPerYear;
         }
-        $daysBetweenIssueAndSettlement = DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis);
+        $daysBetweenIssueAndSettlement = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis));
         if (!is_numeric($daysBetweenIssueAndSettlement)) {
             //    return date error
             return $daysBetweenIssueAndSettlement;
         }
         $daysBetweenIssueAndSettlement *= $daysPerYear;
-        $daysBetweenIssueAndMaturity = DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis);
+        $daysBetweenIssueAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis));
         if (!is_numeric($daysBetweenIssueAndMaturity)) {
             //    return date error
             return $daysBetweenIssueAndMaturity;
         }
         $daysBetweenIssueAndMaturity *= $daysPerYear;
-        $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
+        $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
         if (!is_numeric($daysBetweenSettlementAndMaturity)) {
             //    return date error
             return $daysBetweenSettlementAndMaturity;

+ 10 - 9
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php

@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class TreasuryBill
 {
@@ -38,17 +39,17 @@ class TreasuryBill
         }
 
         if ($discount <= 0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         $daysBetweenSettlementAndMaturity = $maturity - $settlement;
         $daysPerYear = Helpers::daysPerYear(
-            DateTimeExcel\DateParts::year($maturity),
+            Functions::scalar(DateTimeExcel\DateParts::year($maturity)),
             FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
         );
 
         if ($daysBetweenSettlementAndMaturity > $daysPerYear || $daysBetweenSettlementAndMaturity < 0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return (365 * $discount) / (360 - $discount * $daysBetweenSettlementAndMaturity);
@@ -83,22 +84,22 @@ class TreasuryBill
         }
 
         if ($discount <= 0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         $daysBetweenSettlementAndMaturity = $maturity - $settlement;
         $daysPerYear = Helpers::daysPerYear(
-            DateTimeExcel\DateParts::year($maturity),
+            Functions::scalar(DateTimeExcel\DateParts::year($maturity)),
             FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
         );
 
         if ($daysBetweenSettlementAndMaturity > $daysPerYear || $daysBetweenSettlementAndMaturity < 0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         $price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
         if ($price < 0.0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return $price;
@@ -134,12 +135,12 @@ class TreasuryBill
 
         $daysBetweenSettlementAndMaturity = $maturity - $settlement;
         $daysPerYear = Helpers::daysPerYear(
-            DateTimeExcel\DateParts::year($maturity),
+            Functions::scalar(DateTimeExcel\DateParts::year($maturity)),
             FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
         );
 
         if ($daysBetweenSettlementAndMaturity > $daysPerYear || $daysBetweenSettlementAndMaturity < 0) {
-            return Functions::NAN();
+            return ExcelError::NAN();
         }
 
         return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);

+ 11 - 11
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php

@@ -61,17 +61,17 @@ class FormulaParser
     /**
      * Create a new FormulaParser.
      *
-     * @param string $pFormula Formula to parse
+     * @param ?string $formula Formula to parse
      */
-    public function __construct($pFormula = '')
+    public function __construct($formula = '')
     {
         // Check parameters
-        if ($pFormula === null) {
+        if ($formula === null) {
             throw new Exception('Invalid parameter passed: formula');
         }
 
         // Initialise values
-        $this->formula = trim($pFormula);
+        $this->formula = trim($formula);
         // Parse!
         $this->parseToTokens();
     }
@@ -89,15 +89,15 @@ class FormulaParser
     /**
      * Get Token.
      *
-     * @param int $pId Token id
+     * @param int $id Token id
      */
-    public function getToken(int $pId = 0): FormulaToken
+    public function getToken(int $id = 0): FormulaToken
     {
-        if (isset($this->tokens[$pId])) {
-            return $this->tokens[$pId];
+        if (isset($this->tokens[$id])) {
+            return $this->tokens[$id];
         }
 
-        throw new Exception("Token with id $pId does not exist.");
+        throw new Exception("Token with id $id does not exist.");
     }
 
     /**
@@ -490,7 +490,7 @@ class FormulaParser
 
             if (
                 !(
-                (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+                    (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
                 (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
                 ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
                 )
@@ -504,7 +504,7 @@ class FormulaParser
 
             if (
                 !(
-                (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
+                    (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
                 (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
                 ($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
                 )

+ 7 - 7
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php

@@ -76,16 +76,16 @@ class FormulaToken
     /**
      * Create a new FormulaToken.
      *
-     * @param string $pValue
-     * @param string $pTokenType Token type (represented by TOKEN_TYPE_*)
-     * @param string $pTokenSubType Token Subtype (represented by TOKEN_SUBTYPE_*)
+     * @param string $value
+     * @param string $tokenType Token type (represented by TOKEN_TYPE_*)
+     * @param string $tokenSubType Token Subtype (represented by TOKEN_SUBTYPE_*)
      */
-    public function __construct($pValue, $pTokenType = self::TOKEN_TYPE_UNKNOWN, $pTokenSubType = self::TOKEN_SUBTYPE_NOTHING)
+    public function __construct($value, $tokenType = self::TOKEN_TYPE_UNKNOWN, $tokenSubType = self::TOKEN_SUBTYPE_NOTHING)
     {
         // Initialise values
-        $this->value = $pValue;
-        $this->tokenType = $pTokenType;
-        $this->tokenSubType = $pTokenSubType;
+        $this->value = $value;
+        $this->tokenType = $tokenType;
+        $this->tokenSubType = $tokenSubType;
     }
 
     /**

+ 290 - 290
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php

@@ -4,7 +4,6 @@ namespace PhpOffice\PhpSpreadsheet\Calculation;
 
 use PhpOffice\PhpSpreadsheet\Cell\Cell;
 use PhpOffice\PhpSpreadsheet\Shared\Date;
-use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 
 class Functions
 {
@@ -15,12 +14,13 @@ class Functions
      */
     const M_2DIVPI = 0.63661977236758134307553505349006;
 
-    /** constants */
     const COMPATIBILITY_EXCEL = 'Excel';
     const COMPATIBILITY_GNUMERIC = 'Gnumeric';
     const COMPATIBILITY_OPENOFFICE = 'OpenOfficeCalc';
 
+    /** Use of RETURNDATE_PHP_NUMERIC is discouraged - not 32-bit Y2038-safe, no timezone. */
     const RETURNDATE_PHP_NUMERIC = 'P';
+    /** Use of RETURNDATE_UNIX_TIMESTAMP is discouraged - not 32-bit Y2038-safe, no timezone. */
     const RETURNDATE_UNIX_TIMESTAMP = 'P';
     const RETURNDATE_PHP_OBJECT = 'O';
     const RETURNDATE_PHP_DATETIME_OBJECT = 'O';
@@ -40,30 +40,14 @@ class Functions
      */
     protected static $returnDateType = self::RETURNDATE_EXCEL;
 
-    /**
-     * List of error codes.
-     *
-     * @var array
-     */
-    protected static $errorCodes = [
-        'null' => '#NULL!',
-        'divisionbyzero' => '#DIV/0!',
-        'value' => '#VALUE!',
-        'reference' => '#REF!',
-        'name' => '#NAME?',
-        'num' => '#NUM!',
-        'na' => '#N/A',
-        'gettingdata' => '#GETTING_DATA',
-    ];
-
     /**
      * Set the Compatibility Mode.
      *
      * @param string $compatibilityMode Compatibility Mode
-     *                                                Permitted values are:
-     *                                                    Functions::COMPATIBILITY_EXCEL            'Excel'
-     *                                                    Functions::COMPATIBILITY_GNUMERIC        'Gnumeric'
-     *                                                    Functions::COMPATIBILITY_OPENOFFICE    'OpenOfficeCalc'
+     *                                  Permitted values are:
+     *                                      Functions::COMPATIBILITY_EXCEL        'Excel'
+     *                                      Functions::COMPATIBILITY_GNUMERIC     'Gnumeric'
+     *                                      Functions::COMPATIBILITY_OPENOFFICE   'OpenOfficeCalc'
      *
      * @return bool (Success or Failure)
      */
@@ -86,10 +70,10 @@ class Functions
      * Return the current Compatibility Mode.
      *
      * @return string Compatibility Mode
-     *                            Possible Return values are:
-     *                                Functions::COMPATIBILITY_EXCEL            'Excel'
-     *                                Functions::COMPATIBILITY_GNUMERIC        'Gnumeric'
-     *                                Functions::COMPATIBILITY_OPENOFFICE    'OpenOfficeCalc'
+     *                Possible Return values are:
+     *                    Functions::COMPATIBILITY_EXCEL        'Excel'
+     *                    Functions::COMPATIBILITY_GNUMERIC     'Gnumeric'
+     *                    Functions::COMPATIBILITY_OPENOFFICE   'OpenOfficeCalc'
      */
     public static function getCompatibilityMode()
     {
@@ -97,13 +81,13 @@ class Functions
     }
 
     /**
-     * Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object).
+     * Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric or PHP DateTime Object).
      *
      * @param string $returnDateType Return Date Format
-     *                                                Permitted values are:
-     *                                                    Functions::RETURNDATE_UNIX_TIMESTAMP        'P'
-     *                                                    Functions::RETURNDATE_PHP_DATETIME_OBJECT        'O'
-     *                                                    Functions::RETURNDATE_EXCEL            'E'
+     *                               Permitted values are:
+     *                                   Functions::RETURNDATE_UNIX_TIMESTAMP       'P'
+     *                                   Functions::RETURNDATE_PHP_DATETIME_OBJECT  'O'
+     *                                   Functions::RETURNDATE_EXCEL                'E'
      *
      * @return bool Success or failure
      */
@@ -126,10 +110,10 @@ class Functions
      * Return the current Return Date Format for functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object).
      *
      * @return string Return Date Format
-     *                            Possible Return values are:
-     *                                Functions::RETURNDATE_UNIX_TIMESTAMP        'P'
-     *                                Functions::RETURNDATE_PHP_DATETIME_OBJECT        'O'
-     *                                Functions::RETURNDATE_EXCEL            'E'
+     *                Possible Return values are:
+     *                    Functions::RETURNDATE_UNIX_TIMESTAMP         'P'
+     *                    Functions::RETURNDATE_PHP_DATETIME_OBJECT    'O'
+     *                    Functions::RETURNDATE_EXCEL            '     'E'
      */
     public static function getReturnDateType()
     {
@@ -146,92 +130,6 @@ class Functions
         return '#Not Yet Implemented';
     }
 
-    /**
-     * DIV0.
-     *
-     * @return string #Not Yet Implemented
-     */
-    public static function DIV0()
-    {
-        return self::$errorCodes['divisionbyzero'];
-    }
-
-    /**
-     * NA.
-     *
-     * Excel Function:
-     *        =NA()
-     *
-     * Returns the error value #N/A
-     *        #N/A is the error value that means "no value is available."
-     *
-     * @return string #N/A!
-     */
-    public static function NA()
-    {
-        return self::$errorCodes['na'];
-    }
-
-    /**
-     * NaN.
-     *
-     * Returns the error value #NUM!
-     *
-     * @return string #NUM!
-     */
-    public static function NAN()
-    {
-        return self::$errorCodes['num'];
-    }
-
-    /**
-     * NAME.
-     *
-     * Returns the error value #NAME?
-     *
-     * @return string #NAME?
-     */
-    public static function NAME()
-    {
-        return self::$errorCodes['name'];
-    }
-
-    /**
-     * REF.
-     *
-     * Returns the error value #REF!
-     *
-     * @return string #REF!
-     */
-    public static function REF()
-    {
-        return self::$errorCodes['reference'];
-    }
-
-    /**
-     * NULL.
-     *
-     * Returns the error value #NULL!
-     *
-     * @return string #NULL!
-     */
-    public static function null()
-    {
-        return self::$errorCodes['null'];
-    }
-
-    /**
-     * VALUE.
-     *
-     * Returns the error value #VALUE!
-     *
-     * @return string #VALUE!
-     */
-    public static function VALUE()
-    {
-        return self::$errorCodes['value'];
-    }
-
     public static function isMatrixValue($idx)
     {
         return (substr_count($idx, '.') <= 1) || (preg_match('/\.[A-Z]/', $idx) > 0);
@@ -239,7 +137,7 @@ class Functions
 
     public static function isValue($idx)
     {
-        return substr_count($idx, '.') == 0;
+        return substr_count($idx, '.') === 0;
     }
 
     public static function isCellValue($idx)
@@ -254,11 +152,15 @@ class Functions
         if ($condition === '') {
             return '=""';
         }
-        if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='])) {
+        if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='], true)) {
             $condition = self::operandSpecialHandling($condition);
             if (is_bool($condition)) {
                 return '=' . ($condition ? 'TRUE' : 'FALSE');
             } elseif (!is_numeric($condition)) {
+                if ($condition !== '""') { // Not an empty string
+                    // Escape any quotes in the string value
+                    $condition = (string) preg_replace('/"/ui', '""', $condition);
+                }
                 $condition = Calculation::wrapResult(strtoupper($condition));
             }
 
@@ -299,26 +201,142 @@ class Functions
         return $operand;
     }
 
+    /**
+     * NULL.
+     *
+     * Returns the error value #NULL!
+     *
+     * @Deprecated 1.23.0
+     *
+     * @return string #NULL!
+     *
+     *@see Information\ExcelError::null()
+     * Use the null() method in the Information\Error class instead
+     */
+    public static function null()
+    {
+        return Information\ExcelError::null();
+    }
+
+    /**
+     * NaN.
+     *
+     * Returns the error value #NUM!
+     *
+     * @Deprecated 1.23.0
+     *
+     * @return string #NUM!
+     *
+     * @see Information\ExcelError::NAN()
+     * Use the NAN() method in the Information\Error class instead
+     */
+    public static function NAN()
+    {
+        return Information\ExcelError::NAN();
+    }
+
+    /**
+     * REF.
+     *
+     * Returns the error value #REF!
+     *
+     * @Deprecated 1.23.0
+     *
+     * @return string #REF!
+     *
+     * @see Information\ExcelError::REF()
+     * Use the REF() method in the Information\Error class instead
+     */
+    public static function REF()
+    {
+        return Information\ExcelError::REF();
+    }
+
+    /**
+     * NA.
+     *
+     * Excel Function:
+     *        =NA()
+     *
+     * Returns the error value #N/A
+     *        #N/A is the error value that means "no value is available."
+     *
+     * @Deprecated 1.23.0
+     *
+     * @return string #N/A!
+     *
+     * @see Information\ExcelError::NA()
+     * Use the NA() method in the Information\Error class instead
+     */
+    public static function NA()
+    {
+        return Information\ExcelError::NA();
+    }
+
+    /**
+     * VALUE.
+     *
+     * Returns the error value #VALUE!
+     *
+     * @Deprecated 1.23.0
+     *
+     * @return string #VALUE!
+     *
+     * @see Information\ExcelError::VALUE()
+     * Use the VALUE() method in the Information\Error class instead
+     */
+    public static function VALUE()
+    {
+        return Information\ExcelError::VALUE();
+    }
+
+    /**
+     * NAME.
+     *
+     * Returns the error value #NAME?
+     *
+     * @Deprecated 1.23.0
+     *
+     * @return string #NAME?
+     *
+     * @see Information\ExcelError::NAME()
+     * Use the NAME() method in the Information\Error class instead
+     */
+    public static function NAME()
+    {
+        return Information\ExcelError::NAME();
+    }
+
+    /**
+     * DIV0.
+     *
+     * @Deprecated 1.23.0
+     *
+     * @return string #Not Yet Implemented
+     *
+     *@see Information\ExcelError::DIV0()
+     * Use the DIV0() method in the Information\Error class instead
+     */
+    public static function DIV0()
+    {
+        return Information\ExcelError::DIV0();
+    }
+
     /**
      * ERROR_TYPE.
      *
      * @param mixed $value Value to check
      *
-     * @return int|string
+     * @Deprecated 1.23.0
+     *
+     * @return array|int|string
+     *
+     * @see Information\ExcelError::type()
+     * Use the type() method in the Information\Error class instead
      */
     public static function errorType($value = '')
     {
-        $value = self::flattenSingleValue($value);
-
-        $i = 1;
-        foreach (self::$errorCodes as $errorCode) {
-            if ($value === $errorCode) {
-                return $i;
-            }
-            ++$i;
-        }
-
-        return self::NA();
+        return Information\ExcelError::type($value);
     }
 
     /**
@@ -326,15 +344,16 @@ class Functions
      *
      * @param mixed $value Value to check
      *
-     * @return bool
+     * @Deprecated 1.23.0
+     *
+     * @see Information\Value::isBlank()
+     * Use the isBlank() method in the Information\Value class instead
+     *
+     * @return array|bool
      */
     public static function isBlank($value = null)
     {
-        if ($value !== null) {
-            $value = self::flattenSingleValue($value);
-        }
-
-        return $value === null;
+        return Information\Value::isBlank($value);
     }
 
     /**
@@ -342,13 +361,16 @@ class Functions
      *
      * @param mixed $value Value to check
      *
-     * @return bool
+     * @Deprecated 1.23.0
+     *
+     * @see Information\Value::isErr()
+     * Use the isErr() method in the Information\Value class instead
+     *
+     * @return array|bool
      */
     public static function isErr($value = '')
     {
-        $value = self::flattenSingleValue($value);
-
-        return self::isError($value) && (!self::isNa(($value)));
+        return Information\ErrorValue::isErr($value);
     }
 
     /**
@@ -356,17 +378,16 @@ class Functions
      *
      * @param mixed $value Value to check
      *
-     * @return bool
+     * @Deprecated 1.23.0
+     *
+     * @see Information\Value::isError()
+     * Use the isError() method in the Information\Value class instead
+     *
+     * @return array|bool
      */
     public static function isError($value = '')
     {
-        $value = self::flattenSingleValue($value);
-
-        if (!is_string($value)) {
-            return false;
-        }
-
-        return in_array($value, self::$errorCodes);
+        return Information\ErrorValue::isError($value);
     }
 
     /**
@@ -374,13 +395,16 @@ class Functions
      *
      * @param mixed $value Value to check
      *
-     * @return bool
+     * @Deprecated 1.23.0
+     *
+     * @see Information\Value::isNa()
+     * Use the isNa() method in the Information\Value class instead
+     *
+     * @return array|bool
      */
     public static function isNa($value = '')
     {
-        $value = self::flattenSingleValue($value);
-
-        return $value === self::NA();
+        return Information\ErrorValue::isNa($value);
     }
 
     /**
@@ -388,19 +412,16 @@ class Functions
      *
      * @param mixed $value Value to check
      *
-     * @return bool|string
+     * @Deprecated 1.23.0
+     *
+     * @see Information\Value::isEven()
+     * Use the isEven() method in the Information\Value class instead
+     *
+     * @return array|bool|string
      */
     public static function isEven($value = null)
     {
-        $value = self::flattenSingleValue($value);
-
-        if ($value === null) {
-            return self::NAME();
-        } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
-            return self::VALUE();
-        }
-
-        return $value % 2 == 0;
+        return Information\Value::isEven($value);
     }
 
     /**
@@ -408,19 +429,16 @@ class Functions
      *
      * @param mixed $value Value to check
      *
-     * @return bool|string
+     * @Deprecated 1.23.0
+     *
+     * @see Information\Value::isOdd()
+     * Use the isOdd() method in the Information\Value class instead
+     *
+     * @return array|bool|string
      */
     public static function isOdd($value = null)
     {
-        $value = self::flattenSingleValue($value);
-
-        if ($value === null) {
-            return self::NAME();
-        } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
-            return self::VALUE();
-        }
-
-        return abs($value) % 2 == 1;
+        return Information\Value::isOdd($value);
     }
 
     /**
@@ -428,17 +446,16 @@ class Functions
      *
      * @param mixed $value Value to check
      *
-     * @return bool
+     * @Deprecated 1.23.0
+     *
+     * @see Information\Value::isNumber()
+     * Use the isNumber() method in the Information\Value class instead
+     *
+     * @return array|bool
      */
     public static function isNumber($value = null)
     {
-        $value = self::flattenSingleValue($value);
-
-        if (is_string($value)) {
-            return false;
-        }
-
-        return is_numeric($value);
+        return Information\Value::isNumber($value);
     }
 
     /**
@@ -446,13 +463,16 @@ class Functions
      *
      * @param mixed $value Value to check
      *
-     * @return bool
+     * @Deprecated 1.23.0
+     *
+     * @see Information\Value::isLogical()
+     * Use the isLogical() method in the Information\Value class instead
+     *
+     * @return array|bool
      */
     public static function isLogical($value = null)
     {
-        $value = self::flattenSingleValue($value);
-
-        return is_bool($value);
+        return Information\Value::isLogical($value);
     }
 
     /**
@@ -460,13 +480,16 @@ class Functions
      *
      * @param mixed $value Value to check
      *
-     * @return bool
+     * @Deprecated 1.23.0
+     *
+     * @see Information\Value::isText()
+     * Use the isText() method in the Information\Value class instead
+     *
+     * @return array|bool
      */
     public static function isText($value = null)
     {
-        $value = self::flattenSingleValue($value);
-
-        return is_string($value) && !self::isError($value);
+        return Information\Value::isText($value);
     }
 
     /**
@@ -474,11 +497,16 @@ class Functions
      *
      * @param mixed $value Value to check
      *
-     * @return bool
+     * @Deprecated 1.23.0
+     *
+     * @see Information\Value::isNonText()
+     * Use the isNonText() method in the Information\Value class instead
+     *
+     * @return array|bool
      */
     public static function isNonText($value = null)
     {
-        return !self::isText($value);
+        return Information\Value::isNonText($value);
     }
 
     /**
@@ -486,9 +514,14 @@ class Functions
      *
      * Returns a value converted to a number
      *
+     * @Deprecated 1.23.0
+     *
+     * @see Information\Value::asNumber()
+     * Use the asNumber() method in the Information\Value class instead
+     *
      * @param null|mixed $value The value you want converted
      *
-     * @return number N converts values listed in the following table
+     * @return number|string N converts values listed in the following table
      *        If value is or refers to N returns
      *        A number            That number
      *        A date                The serial number of that date
@@ -499,27 +532,7 @@ class Functions
      */
     public static function n($value = null)
     {
-        while (is_array($value)) {
-            $value = array_shift($value);
-        }
-
-        switch (gettype($value)) {
-            case 'double':
-            case 'float':
-            case 'integer':
-                return $value;
-            case 'boolean':
-                return (int) $value;
-            case 'string':
-                //    Errors
-                if ((strlen($value) > 0) && ($value[0] == '#')) {
-                    return $value;
-                }
-
-                break;
-        }
-
-        return 0;
+        return Information\Value::asNumber($value);
     }
 
     /**
@@ -527,6 +540,11 @@ class Functions
      *
      * Returns a number that identifies the type of a value
      *
+     * @Deprecated 1.23.0
+     *
+     * @see Information\Value::type()
+     * Use the type() method in the Information\Value class instead
+     *
      * @param null|mixed $value The value you want tested
      *
      * @return number N converts values listed in the following table
@@ -539,39 +557,7 @@ class Functions
      */
     public static function TYPE($value = null)
     {
-        $value = self::flattenArrayIndexed($value);
-        if (is_array($value) && (count($value) > 1)) {
-            end($value);
-            $a = key($value);
-            //    Range of cells is an error
-            if (self::isCellValue($a)) {
-                return 16;
-            //    Test for Matrix
-            } elseif (self::isMatrixValue($a)) {
-                return 64;
-            }
-        } elseif (empty($value)) {
-            //    Empty Cell
-            return 1;
-        }
-        $value = self::flattenSingleValue($value);
-
-        if (($value === null) || (is_float($value)) || (is_int($value))) {
-            return 1;
-        } elseif (is_bool($value)) {
-            return 4;
-        } elseif (is_array($value)) {
-            return 64;
-        } elseif (is_string($value)) {
-            //    Errors
-            if ((strlen($value) > 0) && ($value[0] == '#')) {
-                return 16;
-            }
-
-            return 2;
-        }
-
-        return 0;
+        return Information\Value::type($value);
     }
 
     /**
@@ -587,24 +573,38 @@ class Functions
             return (array) $array;
         }
 
-        $arrayValues = [];
-        foreach ($array as $value) {
+        $flattened = [];
+        $stack = array_values($array);
+
+        while ($stack) {
+            $value = array_shift($stack);
+
             if (is_array($value)) {
-                foreach ($value as $val) {
-                    if (is_array($val)) {
-                        foreach ($val as $v) {
-                            $arrayValues[] = $v;
-                        }
-                    } else {
-                        $arrayValues[] = $val;
-                    }
-                }
+                array_unshift($stack, ...array_values($value));
             } else {
-                $arrayValues[] = $value;
+                $flattened[] = $value;
             }
         }
 
-        return $arrayValues;
+        return $flattened;
+    }
+
+    /**
+     * @param mixed $value
+     *
+     * @return null|mixed
+     */
+    public static function scalar($value)
+    {
+        if (!is_array($value)) {
+            return $value;
+        }
+
+        do {
+            $value = array_pop($value);
+        } while (is_array($value));
+
+        return $value;
     }
 
     /**
@@ -659,52 +659,52 @@ class Functions
     /**
      * ISFORMULA.
      *
+     * @Deprecated 1.23.0
+     *
+     * @see Information\Value::isFormula()
+     * Use the isFormula() method in the Information\Value class instead
+     *
      * @param mixed $cellReference The cell to check
-     * @param ?Cell $pCell The current cell (containing this formula)
+     * @param ?Cell $cell The current cell (containing this formula)
      *
-     * @return bool|string
+     * @return array|bool|string
      */
-    public static function isFormula($cellReference = '', ?Cell $pCell = null)
+    public static function isFormula($cellReference = '', ?Cell $cell = null)
     {
-        if ($pCell === null) {
-            return self::REF();
-        }
-        $cellReference = self::expandDefinedName((string) $cellReference, $pCell);
-        $cellReference = self::trimTrailingRange($cellReference);
-
-        preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches);
-
-        $cellReference = $matches[6] . $matches[7];
-        $worksheetName = str_replace("''", "'", trim($matches[2], "'"));
-
-        $worksheet = (!empty($worksheetName))
-            ? $pCell->getWorksheet()->getParent()->getSheetByName($worksheetName)
-            : $pCell->getWorksheet();
-
-        return $worksheet->getCell($cellReference)->isFormula();
+        return Information\Value::isFormula($cellReference, $cell);
     }
 
-    public static function expandDefinedName(string $pCoordinate, Cell $pCell): string
+    public static function expandDefinedName(string $coordinate, Cell $cell): string
     {
-        $worksheet = $pCell->getWorksheet();
+        $worksheet = $cell->getWorksheet();
         $spreadsheet = $worksheet->getParent();
         // Uppercase coordinate
-        $pCoordinatex = strtoupper($pCoordinate);
+        $pCoordinatex = strtoupper($coordinate);
         // Eliminate leading equal sign
-        $pCoordinatex = Worksheet::pregReplace('/^=/', '', $pCoordinatex);
+        $pCoordinatex = (string) preg_replace('/^=/', '', $pCoordinatex);
         $defined = $spreadsheet->getDefinedName($pCoordinatex, $worksheet);
         if ($defined !== null) {
             $worksheet2 = $defined->getWorkSheet();
             if (!$defined->isFormula() && $worksheet2 !== null) {
-                $pCoordinate = "'" . $worksheet2->getTitle() . "'!" . Worksheet::pregReplace('/^=/', '', $defined->getValue());
+                $coordinate = "'" . $worksheet2->getTitle() . "'!" .
+                    (string) preg_replace('/^=/', '', str_replace('$', '', $defined->getValue()));
             }
         }
 
-        return $pCoordinate;
+        return $coordinate;
+    }
+
+    public static function trimTrailingRange(string $coordinate): string
+    {
+        return (string) preg_replace('/:[\\w\$]+$/', '', $coordinate);
     }
 
-    public static function trimTrailingRange(string $pCoordinate): string
+    public static function trimSheetFromCellReference(string $coordinate): string
     {
-        return Worksheet::pregReplace('/:[\\w\$]+$/', '', $pCoordinate);
+        if (strpos($coordinate, '!') !== false) {
+            $coordinate = substr($coordinate, strrpos($coordinate, '!') + 1);
+        }
+
+        return $coordinate;
     }
 }

+ 71 - 0
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php

@@ -0,0 +1,71 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Calculation\Information;
+
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
+
+class ErrorValue
+{
+    use ArrayEnabled;
+
+    /**
+     * IS_ERR.
+     *
+     * @param mixed $value Value to check
+     *                      Or can be an array of values
+     *
+     * @return array|bool
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
+     */
+    public static function isErr($value = '')
+    {
+        if (is_array($value)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+        }
+
+        return self::isError($value) && (!self::isNa(($value)));
+    }
+
+    /**
+     * IS_ERROR.
+     *
+     * @param mixed $value Value to check
+     *                      Or can be an array of values
+     *
+     * @return array|bool
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
+     */
+    public static function isError($value = '')
+    {
+        if (is_array($value)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+        }
+
+        if (!is_string($value)) {
+            return false;
+        }
+
+        return in_array($value, ExcelError::ERROR_CODES, true);
+    }
+
+    /**
+     * IS_NA.
+     *
+     * @param mixed $value Value to check
+     *                      Or can be an array of values
+     *
+     * @return array|bool
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
+     */
+    public static function isNa($value = '')
+    {
+        if (is_array($value)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+        }
+
+        return $value === ExcelError::NA();
+    }
+}

+ 171 - 0
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ExcelError.php

@@ -0,0 +1,171 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Calculation\Information;
+
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
+
+class ExcelError
+{
+    use ArrayEnabled;
+
+    /**
+     * List of error codes.
+     *
+     * @var array<string, string>
+     */
+    public const ERROR_CODES = [
+        'null' => '#NULL!', // 1
+        'divisionbyzero' => '#DIV/0!', // 2
+        'value' => '#VALUE!', // 3
+        'reference' => '#REF!', // 4
+        'name' => '#NAME?', // 5
+        'num' => '#NUM!', // 6
+        'na' => '#N/A', // 7
+        'gettingdata' => '#GETTING_DATA', // 8
+        'spill' => '#SPILL!', // 9
+        'connect' => '#CONNECT!', //10
+        'blocked' => '#BLOCKED!', //11
+        'unknown' => '#UNKNOWN!', //12
+        'field' => '#FIELD!', //13
+        'calculation' => '#CALC!', //14
+    ];
+
+    /**
+     * List of error codes. Replaced by constant;
+     * previously it was public and updateable, allowing
+     * user to make inappropriate alterations.
+     *
+     * @deprecated 1.25.0 Use ERROR_CODES constant instead.
+     *
+     * @var array<string, string>
+     */
+    public static $errorCodes = self::ERROR_CODES;
+
+    /**
+     * @param mixed $value
+     */
+    public static function throwError($value): string
+    {
+        return in_array($value, self::ERROR_CODES, true) ? $value : self::ERROR_CODES['value'];
+    }
+
+    /**
+     * ERROR_TYPE.
+     *
+     * @param mixed $value Value to check
+     *
+     * @return array|int|string
+     */
+    public static function type($value = '')
+    {
+        if (is_array($value)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+        }
+
+        $i = 1;
+        foreach (self::ERROR_CODES as $errorCode) {
+            if ($value === $errorCode) {
+                return $i;
+            }
+            ++$i;
+        }
+
+        return self::NA();
+    }
+
+    /**
+     * NULL.
+     *
+     * Returns the error value #NULL!
+     *
+     * @return string #NULL!
+     */
+    public static function null(): string
+    {
+        return self::ERROR_CODES['null'];
+    }
+
+    /**
+     * NaN.
+     *
+     * Returns the error value #NUM!
+     *
+     * @return string #NUM!
+     */
+    public static function NAN(): string
+    {
+        return self::ERROR_CODES['num'];
+    }
+
+    /**
+     * REF.
+     *
+     * Returns the error value #REF!
+     *
+     * @return string #REF!
+     */
+    public static function REF(): string
+    {
+        return self::ERROR_CODES['reference'];
+    }
+
+    /**
+     * NA.
+     *
+     * Excel Function:
+     *        =NA()
+     *
+     * Returns the error value #N/A
+     *        #N/A is the error value that means "no value is available."
+     *
+     * @return string #N/A!
+     */
+    public static function NA(): string
+    {
+        return self::ERROR_CODES['na'];
+    }
+
+    /**
+     * VALUE.
+     *
+     * Returns the error value #VALUE!
+     *
+     * @return string #VALUE!
+     */
+    public static function VALUE(): string
+    {
+        return self::ERROR_CODES['value'];
+    }
+
+    /**
+     * NAME.
+     *
+     * Returns the error value #NAME?
+     *
+     * @return string #NAME?
+     */
+    public static function NAME(): string
+    {
+        return self::ERROR_CODES['name'];
+    }
+
+    /**
+     * DIV0.
+     *
+     * @return string #DIV/0!
+     */
+    public static function DIV0(): string
+    {
+        return self::ERROR_CODES['divisionbyzero'];
+    }
+
+    /**
+     * CALC.
+     *
+     * @return string #CALC!
+     */
+    public static function CALC(): string
+    {
+        return self::ERROR_CODES['calculation'];
+    }
+}

+ 328 - 0
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/Value.php

@@ -0,0 +1,328 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Calculation\Information;
+
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Cell\Cell;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\NamedRange;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class Value
+{
+    use ArrayEnabled;
+
+    /**
+     * IS_BLANK.
+     *
+     * @param mixed $value Value to check
+     *                      Or can be an array of values
+     *
+     * @return array|bool
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
+     */
+    public static function isBlank($value = null)
+    {
+        if (is_array($value)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+        }
+
+        return $value === null;
+    }
+
+    /**
+     * IS_REF.
+     *
+     * @param mixed $value Value to check
+     *
+     * @return bool
+     */
+    public static function isRef($value, ?Cell $cell = null)
+    {
+        if ($cell === null || $value === $cell->getCoordinate()) {
+            return false;
+        }
+
+        $cellValue = Functions::trimTrailingRange($value);
+        if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/ui', $cellValue) === 1) {
+            [$worksheet, $cellValue] = Worksheet::extractSheetTitle($cellValue, true);
+            if (!empty($worksheet) && $cell->getWorksheet()->getParent()->getSheetByName($worksheet) === null) {
+                return false;
+            }
+            [$column, $row] = Coordinate::indexesFromString($cellValue);
+            if ($column > 16384 || $row > 1048576) {
+                return false;
+            }
+
+            return true;
+        }
+
+        $namedRange = $cell->getWorksheet()->getParent()->getNamedRange($value);
+
+        return $namedRange instanceof NamedRange;
+    }
+
+    /**
+     * IS_EVEN.
+     *
+     * @param mixed $value Value to check
+     *                      Or can be an array of values
+     *
+     * @return array|bool|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
+     */
+    public static function isEven($value = null)
+    {
+        if (is_array($value)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+        }
+
+        if ($value === null) {
+            return ExcelError::NAME();
+        } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
+            return ExcelError::VALUE();
+        }
+
+        return ((int) fmod($value, 2)) === 0;
+    }
+
+    /**
+     * IS_ODD.
+     *
+     * @param mixed $value Value to check
+     *                      Or can be an array of values
+     *
+     * @return array|bool|string
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
+     */
+    public static function isOdd($value = null)
+    {
+        if (is_array($value)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+        }
+
+        if ($value === null) {
+            return ExcelError::NAME();
+        } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
+            return ExcelError::VALUE();
+        }
+
+        return ((int) fmod($value, 2)) !== 0;
+    }
+
+    /**
+     * IS_NUMBER.
+     *
+     * @param mixed $value Value to check
+     *                      Or can be an array of values
+     *
+     * @return array|bool
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
+     */
+    public static function isNumber($value = null)
+    {
+        if (is_array($value)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+        }
+
+        if (is_string($value)) {
+            return false;
+        }
+
+        return is_numeric($value);
+    }
+
+    /**
+     * IS_LOGICAL.
+     *
+     * @param mixed $value Value to check
+     *                      Or can be an array of values
+     *
+     * @return array|bool
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
+     */
+    public static function isLogical($value = null)
+    {
+        if (is_array($value)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+        }
+
+        return is_bool($value);
+    }
+
+    /**
+     * IS_TEXT.
+     *
+     * @param mixed $value Value to check
+     *                      Or can be an array of values
+     *
+     * @return array|bool
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
+     */
+    public static function isText($value = null)
+    {
+        if (is_array($value)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+        }
+
+        return is_string($value) && !ErrorValue::isError($value);
+    }
+
+    /**
+     * IS_NONTEXT.
+     *
+     * @param mixed $value Value to check
+     *                      Or can be an array of values
+     *
+     * @return array|bool
+     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
+     */
+    public static function isNonText($value = null)
+    {
+        if (is_array($value)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
+        }
+
+        return !self::isText($value);
+    }
+
+    /**
+     * ISFORMULA.
+     *
+     * @param mixed $cellReference The cell to check
+     * @param ?Cell $cell The current cell (containing this formula)
+     *
+     * @return array|bool|string
+     */
+    public static function isFormula($cellReference = '', ?Cell $cell = null)
+    {
+        if ($cell === null) {
+            return ExcelError::REF();
+        }
+
+        $fullCellReference = Functions::expandDefinedName((string) $cellReference, $cell);
+
+        if (strpos($cellReference, '!') !== false) {
+            $cellReference = Functions::trimSheetFromCellReference($cellReference);
+            $cellReferences = Coordinate::extractAllCellReferencesInRange($cellReference);
+            if (count($cellReferences) > 1) {
+                return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $cellReferences, $cell);
+            }
+        }
+
+        $fullCellReference = Functions::trimTrailingRange($fullCellReference);
+
+        preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $fullCellReference, $matches);
+
+        $fullCellReference = $matches[6] . $matches[7];
+        $worksheetName = str_replace("''", "'", trim($matches[2], "'"));
+
+        $worksheet = (!empty($worksheetName))
+            ? $cell->getWorksheet()->getParent()->getSheetByName($worksheetName)
+            : $cell->getWorksheet();
+
+        return ($worksheet !== null) ? $worksheet->getCell($fullCellReference)->isFormula() : ExcelError::REF();
+    }
+
+    /**
+     * N.
+     *
+     * Returns a value converted to a number
+     *
+     * @param null|mixed $value The value you want converted
+     *
+     * @return number|string N converts values listed in the following table
+     *        If value is or refers to N returns
+     *        A number            That number value
+     *        A date              The Excel serialized number of that date
+     *        TRUE                1
+     *        FALSE               0
+     *        An error value      The error value
+     *        Anything else       0
+     */
+    public static function asNumber($value = null)
+    {
+        while (is_array($value)) {
+            $value = array_shift($value);
+        }
+
+        switch (gettype($value)) {
+            case 'double':
+            case 'float':
+            case 'integer':
+                return $value;
+            case 'boolean':
+                return (int) $value;
+            case 'string':
+                //    Errors
+                if ((strlen($value) > 0) && ($value[0] == '#')) {
+                    return $value;
+                }
+
+                break;
+        }
+
+        return 0;
+    }
+
+    /**
+     * TYPE.
+     *
+     * Returns a number that identifies the type of a value
+     *
+     * @param null|mixed $value The value you want tested
+     *
+     * @return number N converts values listed in the following table
+     *        If value is or refers to N returns
+     *        A number            1
+     *        Text                2
+     *        Logical Value       4
+     *        An error value      16
+     *        Array or Matrix     64
+     */
+    public static function type($value = null)
+    {
+        $value = Functions::flattenArrayIndexed($value);
+        if (is_array($value) && (count($value) > 1)) {
+            end($value);
+            $a = key($value);
+            //    Range of cells is an error
+            if (Functions::isCellValue($a)) {
+                return 16;
+            //    Test for Matrix
+            } elseif (Functions::isMatrixValue($a)) {
+                return 64;
+            }
+        } elseif (empty($value)) {
+            //    Empty Cell
+            return 1;
+        }
+
+        $value = Functions::flattenSingleValue($value);
+        if (($value === null) || (is_float($value)) || (is_int($value))) {
+            return 1;
+        } elseif (is_bool($value)) {
+            return 4;
+        } elseif (is_array($value)) {
+            return 64;
+        } elseif (is_string($value)) {
+            //    Errors
+            if ((strlen($value) > 0) && ($value[0] == '#')) {
+                return 16;
+            }
+
+            return 2;
+        }
+
+        return 0;
+    }
+}

+ 4 - 4
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/WildcardMatch.php

@@ -25,13 +25,13 @@ class WildcardMatch
     public static function wildcard(string $wildcard): string
     {
         // Preg Escape the wildcard, but protecting the Excel * and ? search characters
-        return str_replace(self::SEARCH_SET, self::REPLACEMENT_SET, preg_quote($wildcard));
+        return str_replace(self::SEARCH_SET, self::REPLACEMENT_SET, preg_quote($wildcard, '/'));
     }
 
-    public static function compare(string $value, string $wildcard): bool
+    public static function compare(?string $value, string $wildcard): bool
     {
-        if ($value === '') {
-            return true;
+        if ($value === '' || $value === null) {
+            return false;
         }
 
         return (bool) preg_match("/^{$wildcard}\$/mui", $value);

+ 1 - 1
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical.php

@@ -163,7 +163,7 @@ class Logical
      *
      * @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
      *
-     * @return bool|string the boolean inverse of the argument
+     * @return array|bool|string the boolean inverse of the argument
      */
     public static function NOT($logical = false)
     {

+ 42 - 16
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Conditional.php

@@ -2,11 +2,17 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\Logical;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\Value;
 
 class Conditional
 {
+    use ArrayEnabled;
+
     /**
      * STATEMENT_IF.
      *
@@ -34,21 +40,24 @@ class Conditional
      *
      * @param mixed $condition Condition to evaluate
      * @param mixed $returnIfTrue Value to return when condition is true
+     *              Note that this can be an array value
      * @param mixed $returnIfFalse Optional value to return when condition is false
+     *              Note that this can be an array value
      *
      * @return mixed The value of returnIfTrue or returnIfFalse determined by condition
      */
     public static function statementIf($condition = true, $returnIfTrue = 0, $returnIfFalse = false)
     {
-        if (Functions::isError($condition)) {
+        $condition = ($condition === null) ? true : Functions::flattenSingleValue($condition);
+
+        if (ErrorValue::isError($condition)) {
             return $condition;
         }
 
-        $condition = ($condition === null) ? true : (bool) Functions::flattenSingleValue($condition);
-        $returnIfTrue = ($returnIfTrue === null) ? 0 : Functions::flattenSingleValue($returnIfTrue);
-        $returnIfFalse = ($returnIfFalse === null) ? false : Functions::flattenSingleValue($returnIfFalse);
+        $returnIfTrue = $returnIfTrue ?? 0;
+        $returnIfFalse = $returnIfFalse ?? false;
 
-        return ($condition) ? $returnIfTrue : $returnIfFalse;
+        return ((bool) $condition) ? $returnIfTrue : $returnIfFalse;
     }
 
     /**
@@ -67,9 +76,11 @@ class Conditional
      *        result1, result2, ... result_n
      *              A list of results. The SWITCH function returns the corresponding result when a value
      *              matches expression.
+     *              Note that these can be array values to be returned
      *         default
      *              Optional. It is the default to return if expression does not match any of the values
      *              (value1, value2, ... value_n).
+     *              Note that this can be an array value to be returned
      *
      * @param mixed $arguments Statement arguments
      *
@@ -77,7 +88,7 @@ class Conditional
      */
     public static function statementSwitch(...$arguments)
     {
-        $result = Functions::VALUE();
+        $result = ExcelError::VALUE();
 
         if (count($arguments) > 0) {
             $targetValue = Functions::flattenSingleValue($arguments[0]);
@@ -99,7 +110,7 @@ class Conditional
             }
 
             if ($switchSatisfied !== true) {
-                $result = $hasDefaultClause ? $defaultClause : Functions::NA();
+                $result = $hasDefaultClause ? $defaultClause : ExcelError::NA();
             }
         }
 
@@ -113,16 +124,23 @@ class Conditional
      *        =IFERROR(testValue,errorpart)
      *
      * @param mixed $testValue Value to check, is also the value returned when no error
+     *                      Or can be an array of values
      * @param mixed $errorpart Value to return when testValue is an error condition
+     *              Note that this can be an array value to be returned
      *
      * @return mixed The value of errorpart or testValue determined by error condition
+     *         If an array of values is passed as the $testValue argument, then the returned result will also be
+     *            an array with the same dimensions
      */
     public static function IFERROR($testValue = '', $errorpart = '')
     {
-        $testValue = ($testValue === null) ? '' : Functions::flattenSingleValue($testValue);
-        $errorpart = ($errorpart === null) ? '' : Functions::flattenSingleValue($errorpart);
+        if (is_array($testValue)) {
+            return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $testValue, $errorpart);
+        }
 
-        return self::statementIf(Functions::isError($testValue), $errorpart, $testValue);
+        $errorpart = $errorpart ?? '';
+
+        return self::statementIf(ErrorValue::isError($testValue), $errorpart, $testValue);
     }
 
     /**
@@ -132,16 +150,23 @@ class Conditional
      *        =IFNA(testValue,napart)
      *
      * @param mixed $testValue Value to check, is also the value returned when not an NA
+     *                      Or can be an array of values
      * @param mixed $napart Value to return when testValue is an NA condition
+     *              Note that this can be an array value to be returned
      *
      * @return mixed The value of errorpart or testValue determined by error condition
+     *         If an array of values is passed as the $testValue argument, then the returned result will also be
+     *            an array with the same dimensions
      */
     public static function IFNA($testValue = '', $napart = '')
     {
-        $testValue = ($testValue === null) ? '' : Functions::flattenSingleValue($testValue);
-        $napart = ($napart === null) ? '' : Functions::flattenSingleValue($napart);
+        if (is_array($testValue)) {
+            return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $testValue, $napart);
+        }
+
+        $napart = $napart ?? '';
 
-        return self::statementIf(Functions::isNa($testValue), $napart, $testValue);
+        return self::statementIf(ErrorValue::isNa($testValue), $napart, $testValue);
     }
 
     /**
@@ -156,6 +181,7 @@ class Conditional
      *             Value returned if corresponding testValue (nth) was true
      *
      * @param mixed ...$arguments Statement arguments
+     *              Note that this can be an array value to be returned
      *
      * @return mixed|string The value of returnIfTrue_n, if testValue_n was true. #N/A if none of testValues was true
      */
@@ -164,13 +190,13 @@ class Conditional
         $argumentCount = count($arguments);
 
         if ($argumentCount % 2 != 0) {
-            return Functions::NA();
+            return ExcelError::NA();
         }
         // We use instance of Exception as a falseValue in order to prevent string collision with value in cell
         $falseValueException = new Exception();
         for ($i = 0; $i < $argumentCount; $i += 2) {
             $testValue = ($arguments[$i] === null) ? '' : Functions::flattenSingleValue($arguments[$i]);
-            $returnIfTrue = ($arguments[$i + 1] === null) ? '' : Functions::flattenSingleValue($arguments[$i + 1]);
+            $returnIfTrue = ($arguments[$i + 1] === null) ? '' : $arguments[$i + 1];
             $result = self::statementIf($testValue, $returnIfTrue, $falseValueException);
 
             if ($result !== $falseValueException) {
@@ -178,6 +204,6 @@ class Conditional
             }
         }
 
-        return Functions::NA();
+        return ExcelError::NA();
     }
 }

+ 16 - 7
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php

@@ -2,11 +2,15 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\Logical;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Operations
 {
+    use ArrayEnabled;
+
     /**
      * LOGICAL_AND.
      *
@@ -32,7 +36,7 @@ class Operations
         $args = Functions::flattenArray($args);
 
         if (count($args) == 0) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
 
         $args = array_filter($args, function ($value) {
@@ -73,7 +77,7 @@ class Operations
         $args = Functions::flattenArray($args);
 
         if (count($args) == 0) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
 
         $args = array_filter($args, function ($value) {
@@ -115,7 +119,7 @@ class Operations
         $args = Functions::flattenArray($args);
 
         if (count($args) == 0) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
 
         $args = array_filter($args, function ($value) {
@@ -146,12 +150,17 @@ class Operations
      *            holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
      *
      * @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
+     *                      Or can be an array of values
      *
-     * @return bool|string the boolean inverse of the argument
+     * @return array|bool|string the boolean inverse of the argument
+     *         If an array of values is passed as an argument, then the returned result will also be an array
+     *            with the same dimensions
      */
     public static function NOT($logical = false)
     {
-        $logical = Functions::flattenSingleValue($logical);
+        if (is_array($logical)) {
+            return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $logical);
+        }
 
         if (is_string($logical)) {
             $logical = mb_strtoupper($logical, 'UTF-8');
@@ -161,7 +170,7 @@ class Operations
                 return true;
             }
 
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
 
         return !$logical;
@@ -187,7 +196,7 @@ class Operations
                 } elseif (($arg == 'FALSE') || ($arg == Calculation::getFALSE())) {
                     $arg = false;
                 } else {
-                    return Functions::VALUE();
+                    return ExcelError::VALUE();
                 }
                 $trueValueCount += ($arg != 0);
             }

+ 25 - 25
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php

@@ -41,9 +41,9 @@ class LookupRef
      * @param bool $referenceStyle A logical value that specifies the A1 or R1C1 reference style.
      *                                TRUE or omitted      CELL_ADDRESS returns an A1-style reference
      *                                FALSE                CELL_ADDRESS returns an R1C1-style reference
-     * @param string $sheetText Optional Name of worksheet to use
+     * @param array|string $sheetText Optional Name of worksheet to use
      *
-     * @return string
+     * @return array|string
      */
     public static function cellAddress($row, $column, $relativity = 1, $referenceStyle = true, $sheetText = '')
     {
@@ -158,18 +158,18 @@ class LookupRef
      *
      * @Deprecated 1.18.0
      *
-     * @see LookupRef\Hyperlink::set()
-     *      Use the set() method in the LookupRef\Hyperlink class instead
-     *
      * @param mixed $linkURL Expect string. Value to check, is also the value returned when no error
      * @param mixed $displayName Expect string. Value to return when testValue is an error condition
-     * @param Cell $pCell The cell to set the hyperlink in
+     * @param Cell $cell The cell to set the hyperlink in
      *
      * @return string The value of $displayName (or $linkURL if $displayName was blank)
+     *
+     *@see LookupRef\Hyperlink::set()
+     *      Use the set() method in the LookupRef\Hyperlink class instead
      */
-    public static function HYPERLINK($linkURL = '', $displayName = null, ?Cell $pCell = null)
+    public static function HYPERLINK($linkURL = '', $displayName = null, ?Cell $cell = null)
     {
-        return LookupRef\Hyperlink::set($linkURL, $displayName, $pCell);
+        return LookupRef\Hyperlink::set($linkURL, $displayName, $cell);
     }
 
     /**
@@ -183,19 +183,19 @@ class LookupRef
      *
      * @Deprecated 1.18.0
      *
-     * @see LookupRef\Indirect::INDIRECT()
-     *      Use the INDIRECT() method in the LookupRef\Indirect class instead
-     *
-     * NOTE - INDIRECT() does not yet support the optional a1 parameter introduced in Excel 2010
-     *
      * @param array|string $cellAddress $cellAddress The cell address of the current cell (containing this formula)
-     * @param Cell $pCell The current cell (containing this formula)
+     * @param Cell $cell The current cell (containing this formula)
      *
      * @return array|string An array containing a cell or range of cells, or a string on error
+     *
+     *@see LookupRef\Indirect::INDIRECT()
+     *      Use the INDIRECT() method in the LookupRef\Indirect class instead
+     *
+     * NOTE - INDIRECT() does not yet support the optional a1 parameter introduced in Excel 2010
      */
-    public static function INDIRECT($cellAddress, Cell $pCell)
+    public static function INDIRECT($cellAddress, Cell $cell)
     {
-        return Indirect::INDIRECT($cellAddress, true, $pCell);
+        return Indirect::INDIRECT($cellAddress, true, $cell);
     }
 
     /**
@@ -233,9 +233,9 @@ class LookupRef
      *
      * @return array|string An array containing a cell or range of cells, or a string on error
      */
-    public static function OFFSET($cellAddress = null, $rows = 0, $columns = 0, $height = null, $width = null, ?Cell $pCell = null)
+    public static function OFFSET($cellAddress = null, $rows = 0, $columns = 0, $height = null, $width = null, ?Cell $cell = null)
     {
-        return Offset::OFFSET($cellAddress, $rows, $columns, $height, $width, $pCell);
+        return Offset::OFFSET($cellAddress, $rows, $columns, $height, $width, $cell);
     }
 
     /**
@@ -277,7 +277,7 @@ class LookupRef
      * @param mixed $matchType The number -1, 0, or 1. -1 means above, 0 means exact match, 1 means below.
      *                         If match_type is 1 or -1, the list has to be ordered.
      *
-     * @return int|string The relative position of the found item
+     * @return array|int|string The relative position of the found item
      */
     public static function MATCH($lookupValue, $lookupArray, $matchType = 1)
     {
@@ -401,16 +401,16 @@ class LookupRef
      *
      * @Deprecated 1.18.0
      *
-     * @see LookupRef\Formula::text()
-     *      Use the text() method in the LookupRef\Formula class instead
-     *
      * @param mixed $cellReference The cell to check
-     * @param Cell $pCell The current cell (containing this formula)
+     * @param Cell $cell The current cell (containing this formula)
      *
      * @return string
+     *
+     *@see LookupRef\Formula::text()
+     *      Use the text() method in the LookupRef\Formula class instead
      */
-    public static function FORMULATEXT($cellReference = '', ?Cell $pCell = null)
+    public static function FORMULATEXT($cellReference = '', ?Cell $cell = null)
     {
-        return LookupRef\Formula::text($cellReference, $pCell);
+        return LookupRef\Formula::text($cellReference, $cell);
     }
 }

+ 35 - 9
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Address.php

@@ -2,11 +2,15 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
+use PhpOffice\PhpSpreadsheet\Cell\AddressHelper;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 
 class Address
 {
+    use ArrayEnabled;
+
     public const ADDRESS_ABSOLUTE = 1;
     public const ADDRESS_COLUMN_RELATIVE = 2;
     public const ADDRESS_ROW_RELATIVE = 3;
@@ -24,33 +28,54 @@ class Address
      *        =ADDRESS(row, column, [relativity], [referenceStyle], [sheetText])
      *
      * @param mixed $row Row number (integer) to use in the cell reference
+     *                      Or can be an array of values
      * @param mixed $column Column number (integer) to use in the cell reference
+     *                      Or can be an array of values
      * @param mixed $relativity Integer flag indicating the type of reference to return
      *                             1 or omitted    Absolute
      *                             2               Absolute row; relative column
      *                             3               Relative row; absolute column
      *                             4               Relative
+     *                      Or can be an array of values
      * @param mixed $referenceStyle A logical (boolean) value that specifies the A1 or R1C1 reference style.
      *                                TRUE or omitted    ADDRESS returns an A1-style reference
      *                                FALSE              ADDRESS returns an R1C1-style reference
+     *                      Or can be an array of values
      * @param mixed $sheetName Optional Name of worksheet to use
+     *                      Or can be an array of values
      *
-     * @return string
+     * @return array|string
+     *         If an array of values is passed as the $testValue argument, then the returned result will also be
+     *            an array with the same dimensions
      */
     public static function cell($row, $column, $relativity = 1, $referenceStyle = true, $sheetName = '')
     {
-        $row = Functions::flattenSingleValue($row);
-        $column = Functions::flattenSingleValue($column);
-        $relativity = ($relativity === null) ? 1 : Functions::flattenSingleValue($relativity);
-        $referenceStyle = ($referenceStyle === null) ? true : Functions::flattenSingleValue($referenceStyle);
-        $sheetName = Functions::flattenSingleValue($sheetName);
+        if (
+            is_array($row) || is_array($column) ||
+            is_array($relativity) || is_array($referenceStyle) || is_array($sheetName)
+        ) {
+            return self::evaluateArrayArguments(
+                [self::class, __FUNCTION__],
+                $row,
+                $column,
+                $relativity,
+                $referenceStyle,
+                $sheetName
+            );
+        }
+
+        $relativity = $relativity ?? 1;
+        $referenceStyle = $referenceStyle ?? true;
 
         if (($row < 1) || ($column < 1)) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
 
         $sheetName = self::sheetName($sheetName);
 
+        if (is_int($referenceStyle)) {
+            $referenceStyle = (bool) $referenceStyle;
+        }
         if ((!is_bool($referenceStyle)) || $referenceStyle === self::REFERENCE_STYLE_A1) {
             return self::formatAsA1($row, $column, $relativity, $sheetName);
         }
@@ -92,7 +117,8 @@ class Address
         if (($relativity == self::ADDRESS_ROW_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
             $row = "[{$row}]";
         }
+        [$rowChar, $colChar] = AddressHelper::getRowAndColumnChars();
 
-        return "{$sheetName}R{$row}C{$column}";
+        return "{$sheetName}$rowChar{$row}$colChar{$column}";
     }
 }

+ 15 - 10
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php

@@ -2,13 +2,17 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Calculation\Internal\WildcardMatch;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 
 class ExcelMatch
 {
+    use ArrayEnabled;
+
     public const MATCHTYPE_SMALLEST_VALUE = -1;
     public const MATCHTYPE_FIRST_VALUE = 0;
     public const MATCHTYPE_LARGEST_VALUE = 1;
@@ -26,15 +30,16 @@ class ExcelMatch
      * @param mixed $matchType The number -1, 0, or 1. -1 means above, 0 means exact match, 1 means below.
      *                         If match_type is 1 or -1, the list has to be ordered.
      *
-     * @return int|string The relative position of the found item
+     * @return array|int|string The relative position of the found item
      */
     public static function MATCH($lookupValue, $lookupArray, $matchType = self::MATCHTYPE_LARGEST_VALUE)
     {
+        if (is_array($lookupValue)) {
+            return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $lookupValue, $lookupArray, $matchType);
+        }
+
         $lookupArray = Functions::flattenArray($lookupArray);
-        $lookupValue = Functions::flattenSingleValue($lookupValue);
-        $matchType = ($matchType === null)
-            ? self::MATCHTYPE_LARGEST_VALUE
-            : (int) Functions::flattenSingleValue($matchType);
+        $matchType = (int) ($matchType ?? self::MATCHTYPE_LARGEST_VALUE);
 
         try {
             // Input validation
@@ -79,7 +84,7 @@ class ExcelMatch
         }
 
         // Unsuccessful in finding a match, return #N/A error value
-        return Functions::NA();
+        return ExcelError::NA();
     }
 
     private static function matchFirstValue($lookupArray, $lookupValue)
@@ -149,7 +154,7 @@ class ExcelMatch
     {
         // Lookup_value type has to be number, text, or logical values
         if ((!is_numeric($lookupValue)) && (!is_string($lookupValue)) && (!is_bool($lookupValue))) {
-            throw new Exception(Functions::NA());
+            throw new Exception(ExcelError::NA());
         }
     }
 
@@ -160,7 +165,7 @@ class ExcelMatch
             ($matchType !== self::MATCHTYPE_FIRST_VALUE) &&
             ($matchType !== self::MATCHTYPE_LARGEST_VALUE) && ($matchType !== self::MATCHTYPE_SMALLEST_VALUE)
         ) {
-            throw new Exception(Functions::NA());
+            throw new Exception(ExcelError::NA());
         }
     }
 
@@ -169,7 +174,7 @@ class ExcelMatch
         // Lookup_array should not be empty
         $lookupArraySize = count($lookupArray);
         if ($lookupArraySize <= 0) {
-            throw new Exception(Functions::NA());
+            throw new Exception(ExcelError::NA());
         }
     }
 
@@ -179,7 +184,7 @@ class ExcelMatch
         foreach ($lookupArray as $i => $value) {
             //    check the type of the value
             if ((!is_numeric($value)) && (!is_string($value)) && (!is_bool($value)) && ($value !== null)) {
-                throw new Exception(Functions::NA());
+                throw new Exception(ExcelError::NA());
             }
             // Convert strings to lowercase for case-insensitive testing
             if (is_string($value)) {

+ 81 - 0
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Filter.php

@@ -0,0 +1,81 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
+
+class Filter
+{
+    /**
+     * @param mixed $lookupArray
+     * @param mixed $matchArray
+     * @param mixed $ifEmpty
+     *
+     * @return mixed
+     */
+    public static function filter($lookupArray, $matchArray, $ifEmpty = null)
+    {
+        if (!is_array($matchArray)) {
+            return ExcelError::VALUE();
+        }
+
+        $matchArray = self::enumerateArrayKeys($matchArray);
+
+        $result = (Matrix::isColumnVector($matchArray))
+            ? self::filterByRow($lookupArray, $matchArray)
+            : self::filterByColumn($lookupArray, $matchArray);
+
+        if (empty($result)) {
+            return $ifEmpty ?? ExcelError::CALC();
+        }
+
+        return array_values(array_map('array_values', $result));
+    }
+
+    private static function enumerateArrayKeys(array $sortArray): array
+    {
+        array_walk(
+            $sortArray,
+            function (&$columns): void {
+                if (is_array($columns)) {
+                    $columns = array_values($columns);
+                }
+            }
+        );
+
+        return array_values($sortArray);
+    }
+
+    private static function filterByRow(array $lookupArray, array $matchArray): array
+    {
+        $matchArray = array_values(array_column($matchArray, 0));
+
+        return array_filter(
+            array_values($lookupArray),
+            function ($index) use ($matchArray): bool {
+                return (bool) $matchArray[$index];
+            },
+            ARRAY_FILTER_USE_KEY
+        );
+    }
+
+    private static function filterByColumn(array $lookupArray, array $matchArray): array
+    {
+        $lookupArray = Matrix::transpose($lookupArray);
+
+        if (count($matchArray) === 1) {
+            $matchArray = array_pop($matchArray);
+        }
+
+        array_walk(
+            $matchArray,
+            function (&$value): void {
+                $value = [$value];
+            }
+        );
+
+        $result = self::filterByRow($lookupArray, $matchArray);
+
+        return Matrix::transpose($result);
+    }
+}

+ 8 - 8
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php

@@ -3,7 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Cell\Cell;
 
 class Formula
@@ -12,14 +12,14 @@ class Formula
      * FORMULATEXT.
      *
      * @param mixed $cellReference The cell to check
-     * @param Cell $pCell The current cell (containing this formula)
+     * @param Cell $cell The current cell (containing this formula)
      *
      * @return string
      */
-    public static function text($cellReference = '', ?Cell $pCell = null)
+    public static function text($cellReference = '', ?Cell $cell = null)
     {
-        if ($pCell === null) {
-            return Functions::REF();
+        if ($cell === null) {
+            return ExcelError::REF();
         }
 
         preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches);
@@ -27,15 +27,15 @@ class Formula
         $cellReference = $matches[6] . $matches[7];
         $worksheetName = trim($matches[3], "'");
         $worksheet = (!empty($worksheetName))
-            ? $pCell->getWorksheet()->getParent()->getSheetByName($worksheetName)
-            : $pCell->getWorksheet();
+            ? $cell->getWorksheet()->getParent()->getSheetByName($worksheetName)
+            : $cell->getWorksheet();
 
         if (
             $worksheet === null ||
             !$worksheet->cellExists($cellReference) ||
             !$worksheet->getCell($cellReference)->isFormula()
         ) {
-            return Functions::NA();
+            return ExcelError::NA();
         }
 
         return $worksheet->getCell($cellReference)->getValue();

+ 18 - 13
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php

@@ -2,13 +2,16 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 
 class HLookup extends LookupBase
 {
+    use ArrayEnabled;
+
     /**
      * HLOOKUP
      * The HLOOKUP function searches for value in the top-most row of lookup_array and returns the value
@@ -24,12 +27,15 @@ class HLookup extends LookupBase
      */
     public static function lookup($lookupValue, $lookupArray, $indexNumber, $notExactMatch = true)
     {
-        $lookupValue = Functions::flattenSingleValue($lookupValue);
-        $indexNumber = Functions::flattenSingleValue($indexNumber);
-        $notExactMatch = ($notExactMatch === null) ? true : Functions::flattenSingleValue($notExactMatch);
-        $lookupArray = self::convertLiteralArray($lookupArray);
+        if (is_array($lookupValue)) {
+            return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $lookupValue, $lookupArray, $indexNumber, $notExactMatch);
+        }
+
+        $notExactMatch = (bool) ($notExactMatch ?? true);
 
         try {
+            self::validateLookupArray($lookupArray);
+            $lookupArray = self::convertLiteralArray($lookupArray);
             $indexNumber = self::validateIndexLookup($lookupArray, $indexNumber);
         } catch (Exception $e) {
             return $e->getMessage();
@@ -38,12 +44,12 @@ class HLookup extends LookupBase
         $f = array_keys($lookupArray);
         $firstRow = reset($f);
         if ((!is_array($lookupArray[$firstRow])) || ($indexNumber > count($lookupArray))) {
-            return Functions::REF();
+            return ExcelError::REF();
         }
 
         $firstkey = $f[0] - 1;
         $returnColumn = $firstkey + $indexNumber;
-        $firstColumn = array_shift($f);
+        $firstColumn = array_shift($f) ?? 1;
         $rowNumber = self::hLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
 
         if ($rowNumber !== null) {
@@ -51,24 +57,23 @@ class HLookup extends LookupBase
             return $lookupArray[$returnColumn][Coordinate::stringFromColumnIndex($rowNumber)];
         }
 
-        return Functions::NA();
+        return ExcelError::NA();
     }
 
     /**
      * @param mixed $lookupValue The value that you want to match in lookup_array
-     * @param mixed $column The column to look up
-     * @param mixed $notExactMatch determines if you are looking for an exact match based on lookup_value
+     * @param  int|string $column
      */
-    private static function hLookupSearch($lookupValue, array $lookupArray, $column, $notExactMatch): ?int
+    private static function hLookupSearch($lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
     {
-        $lookupLower = StringHelper::strToLower($lookupValue);
+        $lookupLower = StringHelper::strToLower((string) $lookupValue);
 
         $rowNumber = null;
         foreach ($lookupArray[$column] as $rowKey => $rowData) {
             // break if we have passed possible keys
             $bothNumeric = is_numeric($lookupValue) && is_numeric($rowData);
             $bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData);
-            $cellDataLower = StringHelper::strToLower($rowData);
+            $cellDataLower = StringHelper::strToLower((string) $rowData);
 
             if (
                 $notExactMatch &&

+ 9 - 9
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php

@@ -13,12 +13,12 @@ class Helpers
 
     public const CELLADDRESS_USE_R1C1 = false;
 
-    private static function convertR1C1(string &$cellAddress1, ?string &$cellAddress2, bool $a1): string
+    private static function convertR1C1(string &$cellAddress1, ?string &$cellAddress2, bool $a1, ?int $baseRow = null, ?int $baseCol = null): string
     {
         if ($a1 === self::CELLADDRESS_USE_R1C1) {
-            $cellAddress1 = AddressHelper::convertToA1($cellAddress1);
+            $cellAddress1 = AddressHelper::convertToA1($cellAddress1, $baseRow ?? 1, $baseCol ?? 1);
             if ($cellAddress2) {
-                $cellAddress2 = AddressHelper::convertToA1($cellAddress2);
+                $cellAddress2 = AddressHelper::convertToA1($cellAddress2, $baseRow ?? 1, $baseCol ?? 1);
             }
         }
 
@@ -35,7 +35,7 @@ class Helpers
         }
     }
 
-    public static function extractCellAddresses(string $cellAddress, bool $a1, Worksheet $sheet, string $sheetName = ''): array
+    public static function extractCellAddresses(string $cellAddress, bool $a1, Worksheet $sheet, string $sheetName = '', ?int $baseRow = null, ?int $baseCol = null): array
     {
         $cellAddress1 = $cellAddress;
         $cellAddress2 = null;
@@ -43,7 +43,7 @@ class Helpers
         if ($namedRange !== null) {
             $workSheet = $namedRange->getWorkSheet();
             $sheetTitle = ($workSheet === null) ? '' : $workSheet->getTitle();
-            $value = preg_replace('/^=/', '', $namedRange->getValue());
+            $value = (string) preg_replace('/^=/', '', $namedRange->getValue());
             self::adjustSheetTitle($sheetTitle, $value);
             $cellAddress1 = $sheetTitle . $value;
             $cellAddress = $cellAddress1;
@@ -52,12 +52,12 @@ class Helpers
         if (strpos($cellAddress, ':') !== false) {
             [$cellAddress1, $cellAddress2] = explode(':', $cellAddress);
         }
-        $cellAddress = self::convertR1C1($cellAddress1, $cellAddress2, $a1);
+        $cellAddress = self::convertR1C1($cellAddress1, $cellAddress2, $a1, $baseRow, $baseCol);
 
         return [$cellAddress1, $cellAddress2, $cellAddress];
     }
 
-    public static function extractWorksheet(string $cellAddress, Cell $pCell): array
+    public static function extractWorksheet(string $cellAddress, Cell $cell): array
     {
         $sheetName = '';
         if (strpos($cellAddress, '!') !== false) {
@@ -66,8 +66,8 @@ class Helpers
         }
 
         $worksheet = ($sheetName !== '')
-            ? $pCell->getWorksheet()->getParent()->getSheetByName($sheetName)
-            : $pCell->getWorksheet();
+            ? $cell->getWorksheet()->getParent()->getSheetByName($sheetName)
+            : $cell->getWorksheet();
 
         return [$cellAddress, $worksheet, $sheetName];
     }

+ 7 - 6
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php

@@ -3,6 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Cell\Cell;
 
 class Hyperlink
@@ -15,25 +16,25 @@ class Hyperlink
      *
      * @param mixed $linkURL Expect string. Value to check, is also the value returned when no error
      * @param mixed $displayName Expect string. Value to return when testValue is an error condition
-     * @param Cell $pCell The cell to set the hyperlink in
+     * @param Cell $cell The cell to set the hyperlink in
      *
      * @return mixed The value of $displayName (or $linkURL if $displayName was blank)
      */
-    public static function set($linkURL = '', $displayName = null, ?Cell $pCell = null)
+    public static function set($linkURL = '', $displayName = null, ?Cell $cell = null)
     {
         $linkURL = ($linkURL === null) ? '' : Functions::flattenSingleValue($linkURL);
         $displayName = ($displayName === null) ? '' : Functions::flattenSingleValue($displayName);
 
-        if ((!is_object($pCell)) || (trim($linkURL) == '')) {
-            return Functions::REF();
+        if ((!is_object($cell)) || (trim($linkURL) == '')) {
+            return ExcelError::REF();
         }
 
         if ((is_object($displayName)) || trim($displayName) == '') {
             $displayName = $linkURL;
         }
 
-        $pCell->getHyperlink()->setUrl($linkURL);
-        $pCell->getHyperlink()->setTooltip($displayName);
+        $cell->getHyperlink()->setUrl($linkURL);
+        $cell->getHyperlink()->setTooltip($displayName);
 
         return $displayName;
     }

+ 41 - 9
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php

@@ -5,7 +5,9 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 use Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Cell\Cell;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 
 class Indirect
@@ -23,7 +25,7 @@ class Indirect
             return Helpers::CELLADDRESS_USE_A1;
         }
         if (is_string($a1fmt)) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
 
         return (bool) $a1fmt;
@@ -38,7 +40,7 @@ class Indirect
     {
         $cellAddress = Functions::flattenSingleValue($cellAddress);
         if (!is_string($cellAddress) || !$cellAddress) {
-            throw new Exception(Functions::REF());
+            throw new Exception(ExcelError::REF());
         }
 
         return $cellAddress;
@@ -56,12 +58,14 @@ class Indirect
      * @param array|string $cellAddress $cellAddress The cell address of the current cell (containing this formula)
      * @param mixed $a1fmt Expect bool Helpers::CELLADDRESS_USE_A1 or CELLADDRESS_USE_R1C1,
      *                      but can be provided as numeric which is cast to bool
-     * @param Cell $pCell The current cell (containing this formula)
+     * @param Cell $cell The current cell (containing this formula)
      *
      * @return array|string An array containing a cell or range of cells, or a string on error
      */
-    public static function INDIRECT($cellAddress, $a1fmt, Cell $pCell)
+    public static function INDIRECT($cellAddress, $a1fmt, Cell $cell)
     {
+        [$baseCol, $baseRow] = Coordinate::indexesFromString($cell->getCoordinate());
+
         try {
             $a1 = self::a1Format($a1fmt);
             $cellAddress = self::validateAddress($cellAddress);
@@ -69,15 +73,25 @@ class Indirect
             return $e->getMessage();
         }
 
-        [$cellAddress, $worksheet, $sheetName] = Helpers::extractWorksheet($cellAddress, $pCell);
+        [$cellAddress, $worksheet, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
 
-        [$cellAddress1, $cellAddress2, $cellAddress] = Helpers::extractCellAddresses($cellAddress, $a1, $pCell->getWorkSheet(), $sheetName);
+        if (preg_match('/^' . Calculation::CALCULATION_REGEXP_COLUMNRANGE_RELATIVE . '$/miu', $cellAddress, $matches)) {
+            $cellAddress = self::handleRowColumnRanges($worksheet, ...explode(':', $cellAddress));
+        } elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_ROWRANGE_RELATIVE . '$/miu', $cellAddress, $matches)) {
+            $cellAddress = self::handleRowColumnRanges($worksheet, ...explode(':', $cellAddress));
+        }
+
+        try {
+            [$cellAddress1, $cellAddress2, $cellAddress] = Helpers::extractCellAddresses($cellAddress, $a1, $cell->getWorkSheet(), $sheetName, $baseRow, $baseCol);
+        } catch (Exception $e) {
+            return ExcelError::REF();
+        }
 
         if (
-            (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress1, $matches)) ||
-            (($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress2, $matches)))
+            (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress1, $matches)) ||
+            (($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress2, $matches)))
         ) {
-            return Functions::REF();
+            return ExcelError::REF();
         }
 
         return self::extractRequiredCells($worksheet, $cellAddress);
@@ -94,4 +108,22 @@ class Indirect
         return Calculation::getInstance($worksheet !== null ? $worksheet->getParent() : null)
             ->extractCellRange($cellAddress, $worksheet, false);
     }
+
+    private static function handleRowColumnRanges(?Worksheet $worksheet, string $start, string $end): string
+    {
+        // Being lazy, we're only checking a single row/column to get the max
+        if (ctype_digit($start) && $start <= 1048576) {
+            // Max 16,384 columns for Excel2007
+            $endColRef = ($worksheet !== null) ? $worksheet->getHighestDataColumn((int) $start) : 'XFD';
+
+            return "A{$start}:{$endColRef}{$end}";
+        } elseif (ctype_alpha($start) && strlen($start) <= 3) {
+            // Max 1,048,576 rows for Excel2007
+            $endRowRef = ($worksheet !== null) ? $worksheet->getHighestDataRow($start) : 1048576;
+
+            return "{$start}1:{$end}{$endRowRef}";
+        }
+
+        return "{$start}:{$end}";
+    }
 }

+ 8 - 3
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php

@@ -2,11 +2,14 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
 class Lookup
 {
+    use ArrayEnabled;
+
     /**
      * LOOKUP
      * The LOOKUP function searches for value either from a one-row or one-column range or from an array.
@@ -19,10 +22,12 @@ class Lookup
      */
     public static function lookup($lookupValue, $lookupVector, $resultVector = null)
     {
-        $lookupValue = Functions::flattenSingleValue($lookupValue);
+        if (is_array($lookupValue)) {
+            return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $lookupValue, $lookupVector, $resultVector);
+        }
 
         if (!is_array($lookupVector)) {
-            return Functions::NA();
+            return ExcelError::NA();
         }
         $hasResultVector = isset($resultVector);
         $lookupRows = self::rowCount($lookupVector);

+ 25 - 7
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php

@@ -3,20 +3,38 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 abstract class LookupBase
 {
-    protected static function validateIndexLookup($lookup_array, $index_number)
+    /**
+     * @param mixed $lookup_array
+     */
+    protected static function validateLookupArray($lookup_array): void
     {
-        // index_number must be a number greater than or equal to 1
-        if (!is_numeric($index_number) || $index_number < 1) {
-            throw new Exception(Functions::VALUE());
+        if (!is_array($lookup_array)) {
+            throw new Exception(ExcelError::REF());
+        }
+    }
+
+    protected static function validateIndexLookup(array $lookup_array, $index_number): int
+    {
+        // index_number must be a number greater than or equal to 1.
+        // Excel results are inconsistent when index is non-numeric.
+        // VLOOKUP(whatever, whatever, SQRT(-1)) yields NUM error, but
+        // VLOOKUP(whatever, whatever, cellref) yields REF error
+        //   when cellref is '=SQRT(-1)'. So just try our best here.
+        // Similar results if string (literal yields VALUE, cellRef REF).
+        if (!is_numeric($index_number)) {
+            throw new Exception(ExcelError::throwError($index_number));
+        }
+        if ($index_number < 1) {
+            throw new Exception(ExcelError::VALUE());
         }
 
         // index_number must be less than or equal to the number of columns in lookup_array
         if ((!is_array($lookup_array)) || (empty($lookup_array))) {
-            throw new Exception(Functions::REF());
+            throw new Exception(ExcelError::REF());
         }
 
         return (int) $index_number;
@@ -25,7 +43,7 @@ abstract class LookupBase
     protected static function checkMatch(
         bool $bothNumeric,
         bool $bothNotNumeric,
-        $notExactMatch,
+        bool $notExactMatch,
         int $rowKey,
         string $cellDataLower,
         string $lookupLower,

+ 5 - 4
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php

@@ -3,7 +3,8 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class LookupRefValidations
 {
@@ -13,11 +14,11 @@ class LookupRefValidations
     public static function validateInt($value): int
     {
         if (!is_numeric($value)) {
-            if (Functions::isError($value)) {
+            if (ErrorValue::isError($value)) {
                 throw new Exception($value);
             }
 
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
 
         return (int) floor((float) $value);
@@ -31,7 +32,7 @@ class LookupRefValidations
         $value = self::validateInt($value);
 
         if (($allowZero === false && $value <= 0) || $value < 0) {
-            throw new Exception(Functions::VALUE());
+            throw new Exception(ExcelError::VALUE());
         }
 
         return $value;

+ 33 - 5
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php

@@ -2,11 +2,31 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Matrix
 {
+    use ArrayEnabled;
+
+    /**
+     * Helper function; NOT an implementation of any Excel Function.
+     */
+    public static function isColumnVector(array $values): bool
+    {
+        return count($values, COUNT_RECURSIVE) === (count($values, COUNT_NORMAL) * 2);
+    }
+
+    /**
+     * Helper function; NOT an implementation of any Excel Function.
+     */
+    public static function isRowVector(array $values): bool
+    {
+        return count($values, COUNT_RECURSIVE) > 1 &&
+            (count($values, COUNT_NORMAL) === 1 || count($values, COUNT_RECURSIVE) === count($values, COUNT_NORMAL));
+    }
+
     /**
      * TRANSPOSE.
      *
@@ -45,17 +65,25 @@ class Matrix
      * @param mixed $matrix A range of cells or an array constant
      * @param mixed $rowNum The row in the array or range from which to return a value.
      *                          If row_num is omitted, column_num is required.
+     *                      Or can be an array of values
      * @param mixed $columnNum The column in the array or range from which to return a value.
      *                          If column_num is omitted, row_num is required.
+     *                      Or can be an array of values
      *
      * TODO Provide support for area_num, currently not supported
      *
      * @return mixed the value of a specified cell or array of cells
+     *         If an array of values is passed as the $rowNum and/or $columnNum arguments, then the returned result
+     *            will also be an array with the same dimensions
      */
     public static function index($matrix, $rowNum = 0, $columnNum = 0)
     {
-        $rowNum = ($rowNum === null) ? 0 : Functions::flattenSingleValue($rowNum);
-        $columnNum = ($columnNum === null) ? 0 : Functions::flattenSingleValue($columnNum);
+        if (is_array($rowNum) || is_array($columnNum)) {
+            return self::evaluateArrayArgumentsSubsetFrom([self::class, __FUNCTION__], 1, $matrix, $rowNum, $columnNum);
+        }
+
+        $rowNum = $rowNum ?? 0;
+        $columnNum = $columnNum ?? 0;
 
         try {
             $rowNum = LookupRefValidations::validatePositiveInt($rowNum);
@@ -65,14 +93,14 @@ class Matrix
         }
 
         if (!is_array($matrix) || ($rowNum > count($matrix))) {
-            return Functions::REF();
+            return ExcelError::REF();
         }
 
         $rowKeys = array_keys($matrix);
         $columnKeys = @array_keys($matrix[$rowKeys[0]]);
 
         if ($columnNum > count($columnKeys)) {
-            return Functions::REF();
+            return ExcelError::REF();
         }
 
         if ($columnNum === 0) {

+ 22 - 10
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php

@@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Cell\Cell;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
@@ -40,7 +41,7 @@ class Offset
      *
      * @return array|int|string An array containing a cell or range of cells, or a string on error
      */
-    public static function OFFSET($cellAddress = null, $rows = 0, $columns = 0, $height = null, $width = null, ?Cell $pCell = null)
+    public static function OFFSET($cellAddress = null, $rows = 0, $columns = 0, $height = null, $width = null, ?Cell $cell = null)
     {
         $rows = Functions::flattenSingleValue($rows);
         $columns = Functions::flattenSingleValue($columns);
@@ -48,14 +49,14 @@ class Offset
         $width = Functions::flattenSingleValue($width);
 
         if ($cellAddress === null || $cellAddress === '') {
-            return 0;
+            return ExcelError::VALUE();
         }
 
-        if (!is_object($pCell)) {
-            return Functions::REF();
+        if (!is_object($cell)) {
+            return ExcelError::REF();
         }
 
-        [$cellAddress, $worksheet] = self::extractWorksheet($cellAddress, $pCell);
+        [$cellAddress, $worksheet] = self::extractWorksheet($cellAddress, $cell);
 
         $startCell = $endCell = $cellAddress;
         if (strpos($cellAddress, ':')) {
@@ -69,7 +70,7 @@ class Offset
         $startCellColumn += $columns;
 
         if (($startCellRow <= 0) || ($startCellColumn < 0)) {
-            return Functions::REF();
+            return ExcelError::REF();
         }
 
         $endCellColumn = self::adjustEndCellColumnForWidth($endCellColumn, $width, $startCellColumn, $columns);
@@ -78,7 +79,7 @@ class Offset
         $endCellRow = self::adustEndCellRowForHeight($height, $startCellRow, $rows, $endCellRow);
 
         if (($endCellRow <= 0) || ($endCellColumn < 0)) {
-            return Functions::REF();
+            return ExcelError::REF();
         }
         $endCellColumn = Coordinate::stringFromColumnIndex($endCellColumn + 1);
 
@@ -96,8 +97,10 @@ class Offset
             ->extractCellRange($cellAddress, $worksheet, false);
     }
 
-    private static function extractWorksheet($cellAddress, Cell $pCell): array
+    private static function extractWorksheet($cellAddress, Cell $cell): array
     {
+        $cellAddress = self::assessCellAddress($cellAddress, $cell);
+
         $sheetName = '';
         if (strpos($cellAddress, '!') !== false) {
             [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
@@ -105,12 +108,21 @@ class Offset
         }
 
         $worksheet = ($sheetName !== '')
-            ? $pCell->getWorksheet()->getParent()->getSheetByName($sheetName)
-            : $pCell->getWorksheet();
+            ? $cell->getWorksheet()->getParent()->getSheetByName($sheetName)
+            : $cell->getWorksheet();
 
         return [$cellAddress, $worksheet];
     }
 
+    private static function assessCellAddress(string $cellAddress, Cell $cell): string
+    {
+        if (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $cellAddress) !== false) {
+            $cellAddress = Functions::expandDefinedName($cellAddress, $cell);
+        }
+
+        return $cellAddress;
+    }
+
     private static function adjustEndCellColumnForWidth(string $endCellColumn, $width, int $startCellColumn, $columns)
     {
         $endCellColumn = Coordinate::columnIndexFromString($endCellColumn) - 1;

+ 25 - 25
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php

@@ -3,7 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Cell\Cell;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
@@ -20,9 +20,9 @@ class RowColumnInformation
         return $cellAddress === null || (!is_array($cellAddress) && trim($cellAddress) === '');
     }
 
-    private static function cellColumn(?Cell $pCell): int
+    private static function cellColumn(?Cell $cell): int
     {
-        return ($pCell !== null) ? (int) Coordinate::columnIndexFromString($pCell->getColumn()) : 1;
+        return ($cell !== null) ? (int) Coordinate::columnIndexFromString($cell->getColumn()) : 1;
     }
 
     /**
@@ -42,32 +42,32 @@ class RowColumnInformation
      *
      * @return int|int[]
      */
-    public static function COLUMN($cellAddress = null, ?Cell $pCell = null)
+    public static function COLUMN($cellAddress = null, ?Cell $cell = null)
     {
         if (self::cellAddressNullOrWhitespace($cellAddress)) {
-            return self::cellColumn($pCell);
+            return self::cellColumn($cell);
         }
 
         if (is_array($cellAddress)) {
             foreach ($cellAddress as $columnKey => $value) {
-                $columnKey = preg_replace('/[^a-z]/i', '', $columnKey);
+                $columnKey = (string) preg_replace('/[^a-z]/i', '', $columnKey);
 
                 return (int) Coordinate::columnIndexFromString($columnKey);
             }
 
-            return self::cellColumn($pCell);
+            return self::cellColumn($cell);
         }
 
         $cellAddress = $cellAddress ?? '';
-        if ($pCell != null) {
-            [,, $sheetName] = Helpers::extractWorksheet($cellAddress, $pCell);
-            [,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $pCell->getWorksheet(), $sheetName);
+        if ($cell != null) {
+            [,, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
+            [,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $cell->getWorksheet(), $sheetName);
         }
         [, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
         if (strpos($cellAddress, ':') !== false) {
             [$startAddress, $endAddress] = explode(':', $cellAddress);
-            $startAddress = preg_replace('/[^a-z]/i', '', $startAddress);
-            $endAddress = preg_replace('/[^a-z]/i', '', $endAddress);
+            $startAddress = (string) preg_replace('/[^a-z]/i', '', $startAddress);
+            $endAddress = (string) preg_replace('/[^a-z]/i', '', $endAddress);
 
             return range(
                 (int) Coordinate::columnIndexFromString($startAddress),
@@ -75,7 +75,7 @@ class RowColumnInformation
             );
         }
 
-        $cellAddress = preg_replace('/[^a-z]/i', '', $cellAddress);
+        $cellAddress = (string) preg_replace('/[^a-z]/i', '', $cellAddress);
 
         return (int) Coordinate::columnIndexFromString($cellAddress);
     }
@@ -99,7 +99,7 @@ class RowColumnInformation
             return 1;
         }
         if (!is_array($cellAddress)) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
 
         reset($cellAddress);
@@ -113,9 +113,9 @@ class RowColumnInformation
         return $columns;
     }
 
-    private static function cellRow(?Cell $pCell): int
+    private static function cellRow(?Cell $cell): int
     {
-        return ($pCell !== null) ? $pCell->getRow() : 1;
+        return ($cell !== null) ? $cell->getRow() : 1;
     }
 
     /**
@@ -135,10 +135,10 @@ class RowColumnInformation
      *
      * @return int|mixed[]|string
      */
-    public static function ROW($cellAddress = null, ?Cell $pCell = null)
+    public static function ROW($cellAddress = null, ?Cell $cell = null)
     {
         if (self::cellAddressNullOrWhitespace($cellAddress)) {
-            return self::cellRow($pCell);
+            return self::cellRow($cell);
         }
 
         if (is_array($cellAddress)) {
@@ -148,19 +148,19 @@ class RowColumnInformation
                 }
             }
 
-            return self::cellRow($pCell);
+            return self::cellRow($cell);
         }
 
         $cellAddress = $cellAddress ?? '';
-        if ($pCell !== null) {
-            [,, $sheetName] = Helpers::extractWorksheet($cellAddress, $pCell);
-            [,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $pCell->getWorksheet(), $sheetName);
+        if ($cell !== null) {
+            [,, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
+            [,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $cell->getWorksheet(), $sheetName);
         }
         [, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
         if (strpos($cellAddress, ':') !== false) {
             [$startAddress, $endAddress] = explode(':', $cellAddress);
-            $startAddress = preg_replace('/\D/', '', $startAddress);
-            $endAddress = preg_replace('/\D/', '', $endAddress);
+            $startAddress = (string) preg_replace('/\D/', '', $startAddress);
+            $endAddress = (string) preg_replace('/\D/', '', $endAddress);
 
             return array_map(
                 function ($value) {
@@ -193,7 +193,7 @@ class RowColumnInformation
             return 1;
         }
         if (!is_array($cellAddress)) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
 
         reset($cellAddress);

+ 12 - 7
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php

@@ -2,10 +2,14 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Selection
 {
+    use ArrayEnabled;
+
     /**
      * CHOOSE.
      *
@@ -15,26 +19,27 @@ class Selection
      * Excel Function:
      *        =CHOOSE(index_num, value1, [value2], ...)
      *
+     * @param mixed $chosenEntry The entry to select from the list (indexed from 1)
      * @param mixed ...$chooseArgs Data values
      *
      * @return mixed The selected value
      */
-    public static function choose(...$chooseArgs)
+    public static function choose($chosenEntry, ...$chooseArgs)
     {
-        $chosenEntry = Functions::flattenArray(array_shift($chooseArgs));
-        $entryCount = count($chooseArgs) - 1;
-
         if (is_array($chosenEntry)) {
-            $chosenEntry = array_shift($chosenEntry);
+            return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $chosenEntry, ...$chooseArgs);
         }
+
+        $entryCount = count($chooseArgs) - 1;
+
         if (is_numeric($chosenEntry)) {
             --$chosenEntry;
         } else {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
         $chosenEntry = floor($chosenEntry);
         if (($chosenEntry < 0) || ($chosenEntry > $entryCount)) {
-            return Functions::VALUE();
+            return ExcelError::VALUE();
         }
 
         if (is_array($chooseArgs[$chosenEntry])) {

+ 342 - 0
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php

@@ -0,0 +1,342 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Calculation\Exception;
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+
+class Sort extends LookupRefValidations
+{
+    public const ORDER_ASCENDING = 1;
+    public const ORDER_DESCENDING = -1;
+
+    /**
+     * SORT
+     * The SORT function returns a sorted array of the elements in an array.
+     * The returned array is the same shape as the provided array argument.
+     * Both $sortIndex and $sortOrder can be arrays, to provide multi-level sorting.
+     *
+     * @param mixed $sortArray The range of cells being sorted
+     * @param mixed $sortIndex The column or row number within the sortArray to sort on
+     * @param mixed $sortOrder Flag indicating whether to sort ascending or descending
+     *                          Ascending = 1 (self::ORDER_ASCENDING)
+     *                          Descending = -1 (self::ORDER_DESCENDING)
+     * @param mixed $byColumn Whether the sort should be determined by row (the default) or by column
+     *
+     * @return mixed The sorted values from the sort range
+     */
+    public static function sort($sortArray, $sortIndex = 1, $sortOrder = self::ORDER_ASCENDING, $byColumn = false)
+    {
+        if (!is_array($sortArray)) {
+            // Scalars are always returned "as is"
+            return $sortArray;
+        }
+
+        $sortArray = self::enumerateArrayKeys($sortArray);
+
+        $byColumn = (bool) $byColumn;
+        $lookupIndexSize = $byColumn ? count($sortArray) : count($sortArray[0]);
+
+        try {
+            // If $sortIndex and $sortOrder are scalars, then convert them into arrays
+            if (is_scalar($sortIndex)) {
+                $sortIndex = [$sortIndex];
+                $sortOrder = is_scalar($sortOrder) ? [$sortOrder] : $sortOrder;
+            }
+            // but the values of those array arguments still need validation
+            $sortOrder = (empty($sortOrder) ? [self::ORDER_ASCENDING] : $sortOrder);
+            self::validateArrayArgumentsForSort($sortIndex, $sortOrder, $lookupIndexSize);
+        } catch (Exception $e) {
+            return $e->getMessage();
+        }
+
+        // We want a simple, enumrated array of arrays where we can reference column by its index number.
+        $sortArray = array_values(array_map('array_values', $sortArray));
+
+        return ($byColumn === true)
+            ? self::sortByColumn($sortArray, $sortIndex, $sortOrder)
+            : self::sortByRow($sortArray, $sortIndex, $sortOrder);
+    }
+
+    /**
+     * SORTBY
+     * The SORTBY function sorts the contents of a range or array based on the values in a corresponding range or array.
+     * The returned array is the same shape as the provided array argument.
+     * Both $sortIndex and $sortOrder can be arrays, to provide multi-level sorting.
+     *
+     * @param mixed $sortArray The range of cells being sorted
+     * @param mixed $args
+     *              At least one additional argument must be provided, The vector or range to sort on
+     *              After that, arguments are passed as pairs:
+     *                    sort order: ascending or descending
+     *                         Ascending = 1 (self::ORDER_ASCENDING)
+     *                         Descending = -1 (self::ORDER_DESCENDING)
+     *                    additional arrays or ranges for multi-level sorting
+     *
+     * @return mixed The sorted values from the sort range
+     */
+    public static function sortBy($sortArray, ...$args)
+    {
+        if (!is_array($sortArray)) {
+            // Scalars are always returned "as is"
+            return $sortArray;
+        }
+
+        $sortArray = self::enumerateArrayKeys($sortArray);
+
+        $lookupArraySize = count($sortArray);
+        $argumentCount = count($args);
+
+        try {
+            $sortBy = $sortOrder = [];
+            for ($i = 0; $i < $argumentCount; $i += 2) {
+                $sortBy[] = self::validateSortVector($args[$i], $lookupArraySize);
+                $sortOrder[] = self::validateSortOrder($args[$i + 1] ?? self::ORDER_ASCENDING);
+            }
+        } catch (Exception $e) {
+            return $e->getMessage();
+        }
+
+        return self::processSortBy($sortArray, $sortBy, $sortOrder);
+    }
+
+    private static function enumerateArrayKeys(array $sortArray): array
+    {
+        array_walk(
+            $sortArray,
+            function (&$columns): void {
+                if (is_array($columns)) {
+                    $columns = array_values($columns);
+                }
+            }
+        );
+
+        return array_values($sortArray);
+    }
+
+    /**
+     * @param mixed $sortIndex
+     * @param mixed $sortOrder
+     */
+    private static function validateScalarArgumentsForSort(&$sortIndex, &$sortOrder, int $sortArraySize): void
+    {
+        if (is_array($sortIndex) || is_array($sortOrder)) {
+            throw new Exception(ExcelError::VALUE());
+        }
+
+        $sortIndex = self::validatePositiveInt($sortIndex, false);
+
+        if ($sortIndex > $sortArraySize) {
+            throw new Exception(ExcelError::VALUE());
+        }
+
+        $sortOrder = self::validateSortOrder($sortOrder);
+    }
+
+    /**
+     * @param mixed $sortVector
+     */
+    private static function validateSortVector($sortVector, int $sortArraySize): array
+    {
+        if (!is_array($sortVector)) {
+            throw new Exception(ExcelError::VALUE());
+        }
+
+        // It doesn't matter if it's a row or a column vectors, it works either way
+        $sortVector = Functions::flattenArray($sortVector);
+        if (count($sortVector) !== $sortArraySize) {
+            throw new Exception(ExcelError::VALUE());
+        }
+
+        return $sortVector;
+    }
+
+    /**
+     * @param mixed $sortOrder
+     */
+    private static function validateSortOrder($sortOrder): int
+    {
+        $sortOrder = self::validateInt($sortOrder);
+        if (($sortOrder == self::ORDER_ASCENDING || $sortOrder === self::ORDER_DESCENDING) === false) {
+            throw new Exception(ExcelError::VALUE());
+        }
+
+        return $sortOrder;
+    }
+
+    /**
+     * @param array $sortIndex
+     * @param mixed $sortOrder
+     */
+    private static function validateArrayArgumentsForSort(&$sortIndex, &$sortOrder, int $sortArraySize): void
+    {
+        // It doesn't matter if they're row or column vectors, it works either way
+        $sortIndex = Functions::flattenArray($sortIndex);
+        $sortOrder = Functions::flattenArray($sortOrder);
+
+        if (
+            count($sortOrder) === 0 || count($sortOrder) > $sortArraySize ||
+            (count($sortOrder) > count($sortIndex))
+        ) {
+            throw new Exception(ExcelError::VALUE());
+        }
+
+        if (count($sortIndex) > count($sortOrder)) {
+            // If $sortOrder has fewer elements than $sortIndex, then the last order element is repeated.
+            $sortOrder = array_merge(
+                $sortOrder,
+                array_fill(0, count($sortIndex) - count($sortOrder), array_pop($sortOrder))
+            );
+        }
+
+        foreach ($sortIndex as $key => &$value) {
+            self::validateScalarArgumentsForSort($value, $sortOrder[$key], $sortArraySize);
+        }
+    }
+
+    private static function prepareSortVectorValues(array $sortVector): array
+    {
+        // Strings should be sorted case-insensitive; with booleans converted to locale-strings
+        return array_map(
+            function ($value) {
+                if (is_bool($value)) {
+                    return ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
+                } elseif (is_string($value)) {
+                    return StringHelper::strToLower($value);
+                }
+
+                return $value;
+            },
+            $sortVector
+        );
+    }
+
+    /**
+     * @param array[] $sortIndex
+     * @param int[] $sortOrder
+     */
+    private static function processSortBy(array $sortArray, array $sortIndex, $sortOrder): array
+    {
+        $sortArguments = [];
+        $sortData = [];
+        foreach ($sortIndex as $index => $sortValues) {
+            $sortData[] = $sortValues;
+            $sortArguments[] = self::prepareSortVectorValues($sortValues);
+            $sortArguments[] = $sortOrder[$index] === self::ORDER_ASCENDING ? SORT_ASC : SORT_DESC;
+        }
+        $sortArguments = self::applyPHP7Patch($sortArray, $sortArguments);
+
+        $sortVector = self::executeVectorSortQuery($sortData, $sortArguments);
+
+        return self::sortLookupArrayFromVector($sortArray, $sortVector);
+    }
+
+    /**
+     * @param int[] $sortIndex
+     * @param int[] $sortOrder
+     */
+    private static function sortByRow(array $sortArray, array $sortIndex, array $sortOrder): array
+    {
+        $sortVector = self::buildVectorForSort($sortArray, $sortIndex, $sortOrder);
+
+        return self::sortLookupArrayFromVector($sortArray, $sortVector);
+    }
+
+    /**
+     * @param int[] $sortIndex
+     * @param int[] $sortOrder
+     */
+    private static function sortByColumn(array $sortArray, array $sortIndex, array $sortOrder): array
+    {
+        $sortArray = Matrix::transpose($sortArray);
+        $result = self::sortByRow($sortArray, $sortIndex, $sortOrder);
+
+        return Matrix::transpose($result);
+    }
+
+    /**
+     * @param int[] $sortIndex
+     * @param int[] $sortOrder
+     */
+    private static function buildVectorForSort(array $sortArray, array $sortIndex, array $sortOrder): array
+    {
+        $sortArguments = [];
+        $sortData = [];
+        foreach ($sortIndex as $index => $sortIndexValue) {
+            $sortValues = array_column($sortArray, $sortIndexValue - 1);
+            $sortData[] = $sortValues;
+            $sortArguments[] = self::prepareSortVectorValues($sortValues);
+            $sortArguments[] = $sortOrder[$index] === self::ORDER_ASCENDING ? SORT_ASC : SORT_DESC;
+        }
+        $sortArguments = self::applyPHP7Patch($sortArray, $sortArguments);
+
+        $sortData = self::executeVectorSortQuery($sortData, $sortArguments);
+
+        return $sortData;
+    }
+
+    private static function executeVectorSortQuery(array $sortData, array $sortArguments): array
+    {
+        $sortData = Matrix::transpose($sortData);
+
+        // We need to set an index that can be retained, as array_multisort doesn't maintain numeric keys.
+        $sortDataIndexed = [];
+        foreach ($sortData as $key => $value) {
+            $sortDataIndexed[Coordinate::stringFromColumnIndex($key + 1)] = $value;
+        }
+        unset($sortData);
+
+        $sortArguments[] = &$sortDataIndexed;
+
+        array_multisort(...$sortArguments);
+
+        // After the sort, we restore the numeric keys that will now be in the correct, sorted order
+        $sortedData = [];
+        foreach (array_keys($sortDataIndexed) as $key) {
+            $sortedData[] = Coordinate::columnIndexFromString($key) - 1;
+        }
+
+        return $sortedData;
+    }
+
+    private static function sortLookupArrayFromVector(array $sortArray, array $sortVector): array
+    {
+        // Building a new array in the correct (sorted) order works; but may be memory heavy for larger arrays
+        $sortedArray = [];
+        foreach ($sortVector as $index) {
+            $sortedArray[] = $sortArray[$index];
+        }
+
+        return $sortedArray;
+
+//        uksort(
+//            $lookupArray,
+//            function (int $a, int $b) use (array $sortVector) {
+//                return $sortVector[$a] <=> $sortVector[$b];
+//            }
+//        );
+//
+//        return $lookupArray;
+    }
+
+    /**
+     * Hack to handle PHP 7:
+     * From PHP 8.0.0, If two members compare as equal in a sort, they retain their original order;
+     *      but prior to PHP 8.0.0, their relative order in the sorted array was undefined.
+     * MS Excel replicates the PHP 8.0.0 behaviour, retaining the original order of matching elements.
+     * To replicate that behaviour with PHP 7, we add an extra sort based on the row index.
+     */
+    private static function applyPHP7Patch(array $sortArray, array $sortArguments): array
+    {
+        if (PHP_VERSION_ID < 80000) {
+            $sortArguments[] = range(1, count($sortArray));
+            $sortArguments[] = SORT_ASC;
+        }
+
+        return $sortArguments;
+    }
+}

+ 141 - 0
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Unique.php

@@ -0,0 +1,141 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+
+class Unique
+{
+    /**
+     * UNIQUE
+     * The UNIQUE function searches for value either from a one-row or one-column range or from an array.
+     *
+     * @param mixed $lookupVector The range of cells being searched
+     * @param mixed $byColumn Whether the uniqueness should be determined by row (the default) or by column
+     * @param mixed $exactlyOnce Whether the function should return only entries that occur just once in the list
+     *
+     * @return mixed The unique values from the search range
+     */
+    public static function unique($lookupVector, $byColumn = false, $exactlyOnce = false)
+    {
+        if (!is_array($lookupVector)) {
+            // Scalars are always returned "as is"
+            return $lookupVector;
+        }
+
+        $byColumn = (bool) $byColumn;
+        $exactlyOnce = (bool) $exactlyOnce;
+
+        return ($byColumn === true)
+            ? self::uniqueByColumn($lookupVector, $exactlyOnce)
+            : self::uniqueByRow($lookupVector, $exactlyOnce);
+    }
+
+    /**
+     * @return mixed
+     */
+    private static function uniqueByRow(array $lookupVector, bool $exactlyOnce)
+    {
+        // When not $byColumn, we count whole rows or values, not individual values
+        //      so implode each row into a single string value
+        array_walk(
+            $lookupVector,
+            function (array &$value): void {
+                $value = implode(chr(0x00), $value);
+            }
+        );
+
+        $result = self::countValuesCaseInsensitive($lookupVector);
+
+        if ($exactlyOnce === true) {
+            $result = self::exactlyOnceFilter($result);
+        }
+
+        if (count($result) === 0) {
+            return ExcelError::CALC();
+        }
+
+        $result = array_keys($result);
+
+        // restore rows from their strings
+        array_walk(
+            $result,
+            function (string &$value): void {
+                $value = explode(chr(0x00), $value);
+            }
+        );
+
+        return (count($result) === 1) ? array_pop($result) : $result;
+    }
+
+    /**
+     * @return mixed
+     */
+    private static function uniqueByColumn(array $lookupVector, bool $exactlyOnce)
+    {
+        $flattenedLookupVector = Functions::flattenArray($lookupVector);
+
+        if (count($lookupVector, COUNT_RECURSIVE) > count($flattenedLookupVector, COUNT_RECURSIVE) + 1) {
+            // We're looking at a full column check (multiple rows)
+            $transpose = Matrix::transpose($lookupVector);
+            $result = self::uniqueByRow($transpose, $exactlyOnce);
+
+            return (is_array($result)) ? Matrix::transpose($result) : $result;
+        }
+
+        $result = self::countValuesCaseInsensitive($flattenedLookupVector);
+
+        if ($exactlyOnce === true) {
+            $result = self::exactlyOnceFilter($result);
+        }
+
+        if (count($result) === 0) {
+            return ExcelError::CALC();
+        }
+
+        $result = array_keys($result);
+
+        return $result;
+    }
+
+    private static function countValuesCaseInsensitive(array $caseSensitiveLookupValues): array
+    {
+        $caseInsensitiveCounts = array_count_values(
+            array_map(
+                function (string $value) {
+                    return StringHelper::strToUpper($value);
+                },
+                $caseSensitiveLookupValues
+            )
+        );
+
+        $caseSensitiveCounts = [];
+        foreach ($caseInsensitiveCounts as $caseInsensitiveKey => $count) {
+            if (is_numeric($caseInsensitiveKey)) {
+                $caseSensitiveCounts[$caseInsensitiveKey] = $count;
+            } else {
+                foreach ($caseSensitiveLookupValues as $caseSensitiveValue) {
+                    if ($caseInsensitiveKey === StringHelper::strToUpper($caseSensitiveValue)) {
+                        $caseSensitiveCounts[$caseSensitiveValue] = $count;
+
+                        break;
+                    }
+                }
+            }
+        }
+
+        return $caseSensitiveCounts;
+    }
+
+    private static function exactlyOnceFilter(array $values): array
+    {
+        return array_filter(
+            $values,
+            function ($value) {
+                return $value === 1;
+            }
+        );
+    }
+}

+ 26 - 14
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php

@@ -2,12 +2,15 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
+use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 
 class VLookup extends LookupBase
 {
+    use ArrayEnabled;
+
     /**
      * VLOOKUP
      * The VLOOKUP function searches for value in the left-most column of lookup_array and returns the value
@@ -23,11 +26,14 @@ class VLookup extends LookupBase
      */
     public static function lookup($lookupValue, $lookupArray, $indexNumber, $notExactMatch = true)
     {
-        $lookupValue = Functions::flattenSingleValue($lookupValue);
-        $indexNumber = Functions::flattenSingleValue($indexNumber);
-        $notExactMatch = ($notExactMatch === null) ? true : Functions::flattenSingleValue($notExactMatch);
+        if (is_array($lookupValue)) {
+            return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $lookupValue, $lookupArray, $indexNumber, $notExactMatch);
+        }
+
+        $notExactMatch = (bool) ($notExactMatch ?? true);
 
         try {
+            self::validateLookupArray($lookupArray);
             $indexNumber = self::validateIndexLookup($lookupArray, $indexNumber);
         } catch (Exception $e) {
             return $e->getMessage();
@@ -36,14 +42,16 @@ class VLookup extends LookupBase
         $f = array_keys($lookupArray);
         $firstRow = array_pop($f);
         if ((!is_array($lookupArray[$firstRow])) || ($indexNumber > count($lookupArray[$firstRow]))) {
-            return Functions::REF();
+            return ExcelError::REF();
         }
         $columnKeys = array_keys($lookupArray[$firstRow]);
         $returnColumn = $columnKeys[--$indexNumber];
-        $firstColumn = array_shift($columnKeys);
+        $firstColumn = array_shift($columnKeys) ?? 1;
 
         if (!$notExactMatch) {
-            uasort($lookupArray, ['self', 'vlookupSort']);
+            /** @var callable */
+            $callable = [self::class, 'vlookupSort'];
+            uasort($lookupArray, $callable);
         }
 
         $rowNumber = self::vLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
@@ -53,15 +61,15 @@ class VLookup extends LookupBase
             return $lookupArray[$rowNumber][$returnColumn];
         }
 
-        return Functions::NA();
+        return ExcelError::NA();
     }
 
-    private static function vlookupSort($a, $b)
+    private static function vlookupSort(array $a, array $b): int
     {
         reset($a);
         $firstColumn = key($a);
-        $aLower = StringHelper::strToLower($a[$firstColumn]);
-        $bLower = StringHelper::strToLower($b[$firstColumn]);
+        $aLower = StringHelper::strToLower((string) $a[$firstColumn]);
+        $bLower = StringHelper::strToLower((string) $b[$firstColumn]);
 
         if ($aLower == $bLower) {
             return 0;
@@ -70,15 +78,19 @@ class VLookup extends LookupBase
         return ($aLower < $bLower) ? -1 : 1;
     }
 
-    private static function vLookupSearch($lookupValue, $lookupArray, $column, $notExactMatch)
+    /**
+     * @param mixed $lookupValue The value that you want to match in lookup_array
+     * @param  int|string $column
+     */
+    private static function vLookupSearch($lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
     {
-        $lookupLower = StringHelper::strToLower($lookupValue);
+        $lookupLower = StringHelper::strToLower((string) $lookupValue);
 
         $rowNumber = null;
         foreach ($lookupArray as $rowKey => $rowData) {
             $bothNumeric = is_numeric($lookupValue) && is_numeric($rowData[$column]);
             $bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData[$column]);
-            $cellDataLower = StringHelper::strToLower($rowData[$column]);
+            $cellDataLower = StringHelper::strToLower((string) $rowData[$column]);
 
             // break if we have passed possible keys
             if (

+ 98 - 98
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php

@@ -20,9 +20,9 @@ class MathTrig
      * @See MathTrig\Arabic::evaluate()
      *      Use the evaluate method in the MathTrig\Arabic class instead
      *
-     * @param string $roman
+     * @param array|string $roman
      *
-     * @return int|string the arabic numberal contrived from the roman numeral
+     * @return array|int|string the arabic numberal contrived from the roman numeral
      */
     public static function ARABIC($roman)
     {
@@ -50,10 +50,10 @@ class MathTrig
      * @See MathTrig\Trig\Tangent::atan2()
      *      Use the atan2 method in the MathTrig\Trig\Tangent class instead
      *
-     * @param float $xCoordinate the x-coordinate of the point
-     * @param float $yCoordinate the y-coordinate of the point
+     * @param array|float $xCoordinate the x-coordinate of the point
+     * @param array|float $yCoordinate the y-coordinate of the point
      *
-     * @return float|string the inverse tangent of the specified x- and y-coordinates, or a string containing an error
+     * @return array|float|string the inverse tangent of the specified x- and y-coordinates, or a string containing an error
      */
     public static function ATAN2($xCoordinate = null, $yCoordinate = null)
     {
@@ -77,7 +77,7 @@ class MathTrig
      * @param float $radix
      * @param int $minLength
      *
-     * @return string the text representation with the given radix (base)
+     * @return array|string the text representation with the given radix (base)
      */
     public static function BASE($number, $radix, $minLength = null)
     {
@@ -100,7 +100,7 @@ class MathTrig
      * @param float $number the number you want to round
      * @param float $significance the multiple to which you want to round
      *
-     * @return float|string Rounded Number, or a string containing an error
+     * @return array|float|string Rounded Number, or a string containing an error
      *
      * @see MathTrig\Ceiling::ceiling()
      *      Use the ceiling() method in the MathTrig\Ceiling class instead
@@ -124,10 +124,10 @@ class MathTrig
      * @see MathTrig\Combinations::withoutRepetition()
      *      Use the withoutRepetition() method in the MathTrig\Combinations class instead
      *
-     * @param int $numObjs Number of different objects
-     * @param int $numInSet Number of objects in each combination
+     * @param array|int $numObjs Number of different objects
+     * @param array|int $numInSet Number of objects in each combination
      *
-     * @return float|int|string Number of combinations, or a string containing an error
+     * @return array|float|int|string Number of combinations, or a string containing an error
      */
     public static function COMBIN($numObjs, $numInSet)
     {
@@ -151,9 +151,9 @@ class MathTrig
      * @see MathTrig\Round::even()
      *      Use the even() method in the MathTrig\Round class instead
      *
-     * @param float $number Number to round
+     * @param array|float $number Number to round
      *
-     * @return float|int|string Rounded Number, or a string containing an error
+     * @return array|float|int|string Rounded Number, or a string containing an error
      */
     public static function EVEN($number)
     {
@@ -184,9 +184,9 @@ class MathTrig
      *
      * @Deprecated 1.18.0
      *
-     * @param float $factVal Factorial Value
+     * @param array|float $factVal Factorial Value
      *
-     * @return float|int|string Factorial, or a string containing an error
+     * @return array|float|int|string Factorial, or a string containing an error
      *
      *@see MathTrig\Factorial::fact()
      *      Use the fact() method in the MathTrig\Factorial class instead
@@ -206,9 +206,9 @@ class MathTrig
      *
      * @Deprecated 1.18.0
      *
-     * @param float $factVal Factorial Value
+     * @param array|float $factVal Factorial Value
      *
-     * @return float|int|string Double Factorial, or a string containing an error
+     * @return array|float|int|string Double Factorial, or a string containing an error
      *
      *@see MathTrig\Factorial::factDouble()
      *      Use the factDouble() method in the MathTrig\Factorial class instead
@@ -231,7 +231,7 @@ class MathTrig
      * @param float $number Number to round
      * @param float $significance Significance
      *
-     * @return float|string Rounded Number, or a string containing an error
+     * @return array|float|string Rounded Number, or a string containing an error
      *
      *@see MathTrig\Floor::floor()
      *      Use the floor() method in the MathTrig\Floor class instead
@@ -255,7 +255,7 @@ class MathTrig
      * @param float $significance Significance
      * @param int $mode direction to round negative numbers
      *
-     * @return float|string Rounded Number, or a string containing an error
+     * @return array|float|string Rounded Number, or a string containing an error
      *
      *@see MathTrig\Floor::math()
      *      Use the math() method in the MathTrig\Floor class instead
@@ -278,7 +278,7 @@ class MathTrig
      * @param float $number Number to round
      * @param float $significance Significance
      *
-     * @return float|string Rounded Number, or a string containing an error
+     * @return array|float|string Rounded Number, or a string containing an error
      *
      *@see MathTrig\Floor::precise()
      *      Use the precise() method in the MathTrig\Floor class instead
@@ -301,9 +301,9 @@ class MathTrig
      * @see MathTrig\IntClass::evaluate()
      *      Use the evaluate() method in the MathTrig\IntClass class instead
      *
-     * @param float $number Number to cast to an integer
+     * @param array|float $number Number to cast to an integer
      *
-     * @return int|string Integer value, or a string containing an error
+     * @return array|int|string Integer value, or a string containing an error
      */
     public static function INT($number)
     {
@@ -375,7 +375,7 @@ class MathTrig
      * @param float $number The positive real number for which you want the logarithm
      * @param float $base The base of the logarithm. If base is omitted, it is assumed to be 10.
      *
-     * @return float|string The result, or a string containing an error
+     * @return array|float|string The result, or a string containing an error
      */
     public static function logBase($number, $base = 10)
     {
@@ -455,7 +455,7 @@ class MathTrig
      * @param int $a Dividend
      * @param int $b Divisor
      *
-     * @return float|int|string Remainder, or a string containing an error
+     * @return array|float|int|string Remainder, or a string containing an error
      */
     public static function MOD($a = 1, $b = 1)
     {
@@ -470,9 +470,9 @@ class MathTrig
      * @Deprecated 1.17.0
      *
      * @param float $number Number to round
-     * @param int $multiple Multiple to which you want to round $number
+     * @param array|int $multiple Multiple to which you want to round $number
      *
-     * @return float|string Rounded Number, or a string containing an error
+     * @return array|float|string Rounded Number, or a string containing an error
      *
      *@see MathTrig\Round::multiple()
      *      Use the multiple() method in the MathTrig\Mround class instead
@@ -511,9 +511,9 @@ class MathTrig
      * @See MathTrig\Round::odd()
      *      Use the odd method in the MathTrig\Round class instead
      *
-     * @param float $number Number to round
+     * @param array|float $number Number to round
      *
-     * @return float|int|string Rounded Number, or a string containing an error
+     * @return array|float|int|string Rounded Number, or a string containing an error
      */
     public static function ODD($number)
     {
@@ -533,7 +533,7 @@ class MathTrig
      * @param float $x
      * @param float $y
      *
-     * @return float|int|string The result, or a string containing an error
+     * @return array|float|int|string The result, or a string containing an error
      */
     public static function POWER($x = 0, $y = 2)
     {
@@ -579,7 +579,7 @@ class MathTrig
      * @param mixed $numerator
      * @param mixed $denominator
      *
-     * @return int|string
+     * @return array|int|string
      */
     public static function QUOTIENT($numerator, $denominator)
     {
@@ -597,7 +597,7 @@ class MathTrig
      * @param int $min Minimal value
      * @param int $max Maximal value
      *
-     * @return float|int|string Random number
+     * @return array|float|int|string Random number
      */
     public static function RAND($min = 0, $max = 0)
     {
@@ -617,7 +617,7 @@ class MathTrig
      * @param mixed $aValue Number to convert
      * @param mixed $style Number indicating one of five possible forms
      *
-     * @return string Roman numeral, or a string containing an error
+     * @return array|string Roman numeral, or a string containing an error
      */
     public static function ROMAN($aValue, $style = 0)
     {
@@ -634,10 +634,10 @@ class MathTrig
      * @See MathTrig\Round::up()
      *      Use the up() method in the MathTrig\Round class instead
      *
-     * @param float $number Number to round
-     * @param int $digits Number of digits to which you want to round $number
+     * @param array|float $number Number to round
+     * @param array|int $digits Number of digits to which you want to round $number
      *
-     * @return float|string Rounded Number, or a string containing an error
+     * @return array|float|string Rounded Number, or a string containing an error
      */
     public static function ROUNDUP($number, $digits)
     {
@@ -654,10 +654,10 @@ class MathTrig
      * @See MathTrig\Round::down()
      *      Use the down() method in the MathTrig\Round class instead
      *
-     * @param float $number Number to round
-     * @param int $digits Number of digits to which you want to round $number
+     * @param array|float $number Number to round
+     * @param array|int $digits Number of digits to which you want to round $number
      *
-     * @return float|string Rounded Number, or a string containing an error
+     * @return array|float|string Rounded Number, or a string containing an error
      */
     public static function ROUNDDOWN($number, $digits)
     {
@@ -679,7 +679,7 @@ class MathTrig
      * @param mixed $m Step
      * @param mixed[] $args An array of coefficients for the Data Series
      *
-     * @return float|string The result, or a string containing an error
+     * @return array|float|string The result, or a string containing an error
      */
     public static function SERIESSUM($x, $n, $m, ...$args)
     {
@@ -697,9 +697,9 @@ class MathTrig
      * @See MathTrig\Sign::evaluate()
      *      Use the evaluate method in the MathTrig\Sign class instead
      *
-     * @param float $number Number to round
+     * @param array|float $number Number to round
      *
-     * @return int|string sign value, or a string containing an error
+     * @return array|int|string sign value, or a string containing an error
      */
     public static function SIGN($number)
     {
@@ -729,9 +729,9 @@ class MathTrig
      * @See MathTrig\Sqrt::sqrt()
      *      Use the pi method in the MathTrig\Sqrt class instead
      *
-     * @param float $number Number
+     * @param array|float $number Number
      *
-     * @return float|string Square Root of Number * Pi, or a string containing an error
+     * @return array|float|string Square Root of Number * Pi, or a string containing an error
      */
     public static function SQRTPI($number)
     {
@@ -941,7 +941,7 @@ class MathTrig
      * @param float $value
      * @param int $digits
      *
-     * @return float|string Truncated value, or a string containing an error
+     * @return array|float|string Truncated value, or a string containing an error
      */
     public static function TRUNC($value = 0, $digits = 0)
     {
@@ -958,9 +958,9 @@ class MathTrig
      * @See MathTrig\Trig\Secant::sec()
      *      Use the sec method in the MathTrig\Trig\Secant class instead
      *
-     * @param float $angle Number
+     * @param array|float $angle Number
      *
-     * @return float|string The secant of the angle
+     * @return array|float|string The secant of the angle
      */
     public static function SEC($angle)
     {
@@ -977,9 +977,9 @@ class MathTrig
      * @See MathTrig\Trig\Secant::sech()
      *      Use the sech method in the MathTrig\Trig\Secant class instead
      *
-     * @param float $angle Number
+     * @param array|float $angle Number
      *
-     * @return float|string The hyperbolic secant of the angle
+     * @return array|float|string The hyperbolic secant of the angle
      */
     public static function SECH($angle)
     {
@@ -996,9 +996,9 @@ class MathTrig
      * @See MathTrig\Trig\Cosecant::csc()
      *      Use the csc method in the MathTrig\Trig\Cosecant class instead
      *
-     * @param float $angle Number
+     * @param array|float $angle Number
      *
-     * @return float|string The cosecant of the angle
+     * @return array|float|string The cosecant of the angle
      */
     public static function CSC($angle)
     {
@@ -1015,9 +1015,9 @@ class MathTrig
      * @See MathTrig\Trig\Cosecant::csch()
      *      Use the csch method in the MathTrig\Trig\Cosecant class instead
      *
-     * @param float $angle Number
+     * @param array|float $angle Number
      *
-     * @return float|string The hyperbolic cosecant of the angle
+     * @return array|float|string The hyperbolic cosecant of the angle
      */
     public static function CSCH($angle)
     {
@@ -1034,9 +1034,9 @@ class MathTrig
      * @See MathTrig\Trig\Cotangent::cot()
      *      Use the cot method in the MathTrig\Trig\Cotangent class instead
      *
-     * @param float $angle Number
+     * @param array|float $angle Number
      *
-     * @return float|string The cotangent of the angle
+     * @return array|float|string The cotangent of the angle
      */
     public static function COT($angle)
     {
@@ -1053,9 +1053,9 @@ class MathTrig
      * @See MathTrig\Trig\Cotangent::coth()
      *      Use the coth method in the MathTrig\Trig\Cotangent class instead
      *
-     * @param float $angle Number
+     * @param array|float $angle Number
      *
-     * @return float|string The hyperbolic cotangent of the angle
+     * @return array|float|string The hyperbolic cotangent of the angle
      */
     public static function COTH($angle)
     {
@@ -1072,9 +1072,9 @@ class MathTrig
      * @See MathTrig\Trig\Cotangent::acot()
      *      Use the acot method in the MathTrig\Trig\Cotangent class instead
      *
-     * @param float $number Number
+     * @param array|float $number Number
      *
-     * @return float|string The arccotangent of the number
+     * @return array|float|string The arccotangent of the number
      */
     public static function ACOT($number)
     {
@@ -1108,9 +1108,9 @@ class MathTrig
      * @See MathTrig\Trig\Cotangent::acoth()
      *      Use the acoth method in the MathTrig\Trig\Cotangent class instead
      *
-     * @param float $number Number
+     * @param array|float $number Number
      *
-     * @return float|string The hyperbolic arccotangent of the number
+     * @return array|float|string The hyperbolic arccotangent of the number
      */
     public static function ACOTH($number)
     {
@@ -1127,10 +1127,10 @@ class MathTrig
      * @See MathTrig\Round::round()
      *      Use the round() method in the MathTrig\Round class instead
      *
-     * @param mixed $number Should be numeric
-     * @param mixed $precision Should be int
+     * @param array|mixed $number Should be numeric
+     * @param array|mixed $precision Should be int
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinROUND($number, $precision)
     {
@@ -1147,9 +1147,9 @@ class MathTrig
      * @See MathTrig\Absolute::evaluate()
      *      Use the evaluate method in the MathTrig\Absolute class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|mixed $number Should be numeric
      *
-     * @return float|int|string Rounded number
+     * @return array|float|int|string Rounded number
      */
     public static function builtinABS($number)
     {
@@ -1166,9 +1166,9 @@ class MathTrig
      *
      * Returns the result of builtin function acos after validating args.
      *
-     * @param mixed $number Should be numeric
+     * @param array|float $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinACOS($number)
     {
@@ -1185,9 +1185,9 @@ class MathTrig
      * @See MathTrig\Trig\Cosine::acosh()
      *      Use the acosh method in the MathTrig\Trig\Cosine class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|float $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinACOSH($number)
     {
@@ -1204,9 +1204,9 @@ class MathTrig
      * @See MathTrig\Trig\Sine::asin()
      *      Use the asin method in the MathTrig\Trig\Sine class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|float $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinASIN($number)
     {
@@ -1223,9 +1223,9 @@ class MathTrig
      * @See MathTrig\Trig\Sine::asinh()
      *      Use the asinh method in the MathTrig\Trig\Sine class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|float $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinASINH($number)
     {
@@ -1242,9 +1242,9 @@ class MathTrig
      * @See MathTrig\Trig\Tangent::atan()
      *      Use the atan method in the MathTrig\Trig\Tangent class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|float $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinATAN($number)
     {
@@ -1261,9 +1261,9 @@ class MathTrig
      * @See MathTrig\Trig\Tangent::atanh()
      *      Use the atanh method in the MathTrig\Trig\Tangent class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|float $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinATANH($number)
     {
@@ -1280,9 +1280,9 @@ class MathTrig
      * @See MathTrig\Trig\Cosine::cos()
      *      Use the cos method in the MathTrig\Trig\Cosine class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|mixed $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinCOS($number)
     {
@@ -1299,9 +1299,9 @@ class MathTrig
      * @See MathTrig\Trig\Cosine::cosh()
      *      Use the cosh method in the MathTrig\Trig\Cosine class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|mixed $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinCOSH($number)
     {
@@ -1318,9 +1318,9 @@ class MathTrig
      * @See MathTrig\Angle::toDegrees()
      *      Use the toDegrees method in the MathTrig\Angle class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|mixed $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinDEGREES($number)
     {
@@ -1337,9 +1337,9 @@ class MathTrig
      * @See MathTrig\Exp::evaluate()
      *      Use the evaluate method in the MathTrig\Exp class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|mixed $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinEXP($number)
     {
@@ -1358,7 +1358,7 @@ class MathTrig
      *
      * @param mixed $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinLN($number)
     {
@@ -1377,7 +1377,7 @@ class MathTrig
      *
      * @param mixed $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinLOG10($number)
     {
@@ -1394,9 +1394,9 @@ class MathTrig
      * @See MathTrig\Angle::toRadians()
      *      Use the toRadians method in the MathTrig\Angle class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|mixed $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinRADIANS($number)
     {
@@ -1413,9 +1413,9 @@ class MathTrig
      * @See MathTrig\Trig\Sine::evaluate()
      *      Use the sin method in the MathTrig\Trig\Sine class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|mixed $number Should be numeric
      *
-     * @return float|string sine
+     * @return array|float|string sine
      */
     public static function builtinSIN($number)
     {
@@ -1432,9 +1432,9 @@ class MathTrig
      * @See MathTrig\Trig\Sine::sinh()
      *      Use the sinh method in the MathTrig\Trig\Sine class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|mixed $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinSINH($number)
     {
@@ -1451,9 +1451,9 @@ class MathTrig
      * @See MathTrig\Sqrt::sqrt()
      *      Use the sqrt method in the MathTrig\Sqrt class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|mixed $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinSQRT($number)
     {
@@ -1470,9 +1470,9 @@ class MathTrig
      * @See MathTrig\Trig\Tangent::tan()
      *      Use the tan method in the MathTrig\Trig\Tangent class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|mixed $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinTAN($number)
     {
@@ -1489,9 +1489,9 @@ class MathTrig
      * @See MathTrig\Trig\Tangent::tanh()
      *      Use the tanh method in the MathTrig\Trig\Tangent class instead
      *
-     * @param mixed $number Should be numeric
+     * @param array|mixed $number Should be numeric
      *
-     * @return float|string Rounded number
+     * @return array|float|string Rounded number
      */
     public static function builtinTANH($number)
     {

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff